// TetiSoft: Modified for use with type1.library

/***************************************************************************/
/*                                                                         */
/*  t1afm.c                                                                */
/*                                                                         */
/*    AFM support for Type 1 fonts (body).                                 */
/*                                                                         */
/*  Copyright 1996-2000 by                                                 */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef T1GST
#include "global.h"
#endif
#include <proto/exec.h>

#include <stdlib.h>  /* for qsort()   */
#include <string.h>  /* for strncmp() */
//#include <ctype.h>   /* for isalnum() */

// TetiSoft: Avoid SAS/C isalnum(), isdigit(), ishdigit() and strtoul(),
// we don't want the global _ctype array included
int isalnum ( int c )
{
	return ( ( ( c >= '0' ) && ( c <= '9' ) ) ||
		 ( ( c >= 'A' ) && ( c <= 'Z' ) ) ||
		 ( ( c >= 'a' ) && ( c <= 'z' ) ) );
}
int isdigit ( int c )
{
	return ( ( c >= '0' ) && ( c <= '9' ) );
}
int ishdigit ( int c )
{
	return ( ( ( c >= '0' ) && ( c <= '9' ) ) ||
		 ( ( c >= 'A' ) && ( c <= 'F' ) ) ||
		 ( ( c >= 'a' ) && ( c <= 'f' ) ) );
}
unsigned long fourhexcharstoul( unsigned char *s )
{
	unsigned long val;
	int i;
	unsigned char c;

	for (i = 0, val = 0; i < 4; i++)
	{
		c = *s++;
		if ( ( c >= '0' ) && ( c <= '9' ) )
			c -= '0';
		else if ( ( c >= 'A' ) && ( c <= 'F' ) )
			c += (10 - 'A');
		else	// checked before
			c += (10 - 'a');
		val = ( val << 4 ) + c;
	}
	return val;
}

// TetiSoft: Avoid SAS/C pow(), we don't want except() included
static double dpowi ( double base, int exp )
{
	if ( exp < 0 )
		return 1.0 / dpowi( base, -exp );
	else if ( exp == 0 )
		return 1.0;
	else
		return base * dpowi( base, exp - 1 );
}

void T1_Done_AFM( T1_AFM*  afm )
{
	if (afm )
	{
		if ( afm->kern_pairs )
			FreeVec( afm->kern_pairs );
		afm->num_pairs = 0;
		if ( afm->track_kern )
			FreeVec( afm->track_kern );
		afm->num_tracks = 0;
		FreeVec( afm );
	}
}


#define IS_KERN_PAIR( p )  ( p[0] == 'K' && p[1] == 'P' )
#define IS_TRACK_KERN( p ) ( ( p[0] == '\r' || p[0] == '\n' ) && \
                             p[1] == 'T' && \
			     p[2] == 'r' && \
			     p[3] == 'a' && \
			     p[4] == 'c' && \
			     p[5] == 'k' && \
			     p[6] == 'K' && \
			     p[7] == 'e' && \
			     p[8] == 'r' && \
			     p[9] == 'n' )

#define IS_ALPHANUM( c )  ( isalnum( c ) || \
                            c == '_'     || \
                            c == '.'     )


/* read a glyph name and return the equivalent glyph index */
static unsigned long afm_atoindex( struct T1GlyphEngine*  engine,
                                   char**                 start,
                                   char*                  limit )
{
  char*           p = *start;
  int             len;
  unsigned long   result = 0;
  char            temp[64];


  /* skip whitespace */
  while ( ( *p == ' ' || *p == '\t' || *p == ':' || *p == ';' ) &&
          p < limit                                             )
    p++;
  *start = p;

  /* now, read glyph name */
  while ( IS_ALPHANUM( *p ) && p < limit )
    p++;

  len = p - *start;

  if ( len > 0 && len < 64 )
  {
    int  n;


    /* copy glyph name to intermediate array */
    memcpy( temp, *start, len );
    temp[len] = 0;

    /* lookup glyph name in face array */
    for ( n = 0; n < ENCSIZE; n++ )
    {

      char*  gname = engine->fontencoding[n].data.valueP;
      int    glen  = engine->fontencoding[n].len;

      if ( gname && glen == len && gname[0] == temp[0] && strncmp( gname, temp, len ) == 0 )
      {
        result = n;
        break;
      }
    }
    if ( !result && ( engine->fontreq.SymbolSet == UNICODE ) )
    {
      /* if uni<CODE> name, extract index from name */
      if ( ( temp[0] == 'u' )  &&
	   ( temp[1] == 'n' )  &&
	   ( temp[2] == 'i' )  &&
	   ishdigit( temp[3] ) &&
	   ishdigit( temp[4] ) &&
	   ishdigit( temp[5] ) &&
	   ishdigit( temp[6] ) )
      {
//	temp[7] = '\0';
//	result = strtoul( &temp[3], NULL, 16 );
	result = fourhexcharstoul( &temp[3] );
      }
      else
      {
	/* search glyph name in AGL, if found return index */
	for ( n = 0; AGL[n].name; n++ )
	{
	  if ( !strcmp( temp, AGL[n].name) )
	  {
	    result = AGL[n].index;
	    break;
	  }
	}
      }
    }
  }
  *start = p;
  return result;
}


/* read an integer */
static
int  afm_atoi( char**  start,
               char*   limit )
{
  char*     p    = *start;
  int       sum  = 0;
  int       sign = 1;


  /* skip everything that is not a number */
  while ( p < limit && !isdigit( *p ) )
  {
    sign = 1;
    if ( *p == '-' )
      sign = -1;

    p++;
  }

  while ( p < limit && isdigit( *p ) )
  {
    sum = sum * 10 + ( *p - '0' );
    p++;
  }
  *start = p;

  return sum * sign;
}


/* read a double */
static
double  afm_atod( char**  start,
                  char*   limit )
{
  char*     p    = *start;
  double    sum  = 0;
  int       sign = 1;


  /* skip everything that is not a number */
  while ( p < limit && !isdigit( *p ) )
  {
    sign = 1;
    if ( *p == '-' )
      sign = -1;

    p++;
  }

  while ( p < limit && isdigit( *p ) )
  {
    sum = sum * 10 + ( *p - '0' );
    p++;
  }

  /* decipoint ? */
  if ( *p == '.' )
  {
    int pos = 0;
    p++;
    while ( p < limit && isdigit( *p ) )
    {
      pos--;
      sum += ( *p - '0' ) * dpowi ( 10.0, pos ) ;
      p++;
    }
  }

  *start = p;

  return sum * sign;
}


#undef  KERN_INDEX
#define KERN_INDEX( g1, g2 )  ( ( (unsigned long)g1 << 16 ) | g2 )


/* compare two kerning pairs */
static
int  compare_kern_pairs( const void*  a,
                         const void*  b )
{
  T1_Kern_Pair*  pair1 = (T1_Kern_Pair*)a;
  T1_Kern_Pair*  pair2 = (T1_Kern_Pair*)b;

  unsigned long  index1 = KERN_INDEX( pair1->glyph1, pair1->glyph2 );
  unsigned long  index2 = KERN_INDEX( pair2->glyph1, pair2->glyph2 );


  return (int) ( index1 - index2 );
}


/* compare two track kernings */
static
int  compare_track_kern( const void*  a,
                         const void*  b )
{
  T1_Track_Kern*  track1 = (T1_Track_Kern*)a;
  T1_Track_Kern*  track2 = (T1_Track_Kern*)b;

  return ( track1->degree - track2->degree );
}


/* parse an AFM file -- for now, only read the kerning */
T1_AFM* T1_Read_AFM( struct T1GlyphEngine*  engine,
                     char*                  start,
                     char*                  limit )
{
  char*           p;
  int             countKP;
  int             countTK;
  T1_Kern_Pair*   pair;
  T1_Track_Kern*  track;
  T1_AFM*         afm;

  /* we are now going to count the occurences of `KP' or `KPX' in */
  /* the AFM file                                                 */
  countKP = 0;
  countTK = 0;
  for ( p = start; p < limit - 10; p++ )
  {
    if ( IS_KERN_PAIR( p ) )
      countKP++;
    if ( IS_TRACK_KERN( p ) )
      countTK++;
  }

  /* Actually, kerning data is simply optional! */
  if ( ( countKP == 0 ) && ( countTK == 0 ) )
    return NULL;

  /* allocate the pairs */
  afm = AllocVec ( sizeof ( *afm ), MEMF_PUBLIC );
  if ( !afm )
    return NULL;

  if ( countKP != 0)
  {
    afm->kern_pairs = AllocVec ( countKP * sizeof ( T1_Kern_Pair), MEMF_PUBLIC );
    if ( !afm->kern_pairs )
    {
      FreeVec ( afm );
      return NULL;
    }
  }
  else
    afm->kern_pairs = NULL;

  if ( countTK != 0)
  {
    afm->track_kern = AllocVec ( countTK * sizeof ( T1_Track_Kern), MEMF_PUBLIC );
    if ( !afm->track_kern )
    {
      if ( afm->kern_pairs )
        FreeVec ( afm->kern_pairs );
      FreeVec ( afm );
      return NULL;
    }
  }
  else
    afm->track_kern = NULL;

  /* now, read each kern pair / track kern */
  pair            = afm->kern_pairs;
  afm->num_pairs  = countKP;
  track           = afm->track_kern;
  afm->num_tracks = countTK;

  for ( p = start; p < limit - 10; p++ )
  {
    if ( IS_KERN_PAIR( p ) )
    {
      char*  q;

      /* skip keyword (KP or KPX) */
      q = p + 2;
      if ( *q == 'X' )
        q++;

      pair->glyph1    = afm_atoindex( engine, &q, limit );
      pair->glyph2    = afm_atoindex( engine, &q, limit );
      pair->kerning.x = afm_atoi( &q, limit );

      pair->kerning.y = 0;
      if ( p[2] != 'X' )
        pair->kerning.y = afm_atoi( &q, limit );

      pair++;
    }
    else if ( IS_TRACK_KERN( p ) )
    {
      char*  q;

      /* skip keyword (\nTrackKern) */
      q = p + 10;

      track->degree    = afm_atoi( &q, limit );
      track->minptsize = afm_atoi( &q, limit );
      track->minkern   = afm_atod( &q, limit );
      track->maxptsize = afm_atoi( &q, limit );
      track->maxkern   = afm_atod( &q, limit );

      track++;
    }
  }

  /* now, sort the kern pairs according to their glyph indices */
  if ( countKP )
    qsort( afm->kern_pairs, countKP, sizeof ( T1_Kern_Pair ),
           compare_kern_pairs );

  /* now, sort the track kern according to the degrees */
  if ( countTK )
    qsort( afm->track_kern, countTK, sizeof ( T1_Track_Kern ),
           compare_track_kern );

  return afm;
}


/* find the kerning for a given glyph pair */
void  T1_Get_Kerning( struct T1GlyphEngine*  engine,
		      T1_AFM*		     afm,
                      unsigned long	     glyph1,
                      unsigned long	     glyph2,
                      int		     useTight,
                      int		     pointsize,
                      FT_Vector*	     kerning )
{
  T1_Kern_Pair  *min, *mid, *max;
  unsigned long index = KERN_INDEX( glyph1, glyph2 );

  kerning->x = 0;
  kerning->y = 0;

 if ( (glyph1 < FIRSTCOL)   ||
      (glyph1 & 0xffff0000) ||
      (glyph2 < FIRSTCOL)   ||
      (glyph2 & 0xffff0000) )
	return;

  if ( (engine->fontreq.SymbolSet != UNICODE) &&
       ( (glyph1 > LASTCOL) || (glyph2 > LASTCOL) ) )
	return;

  if ( afm->num_pairs )
  {
    /* simple binary search */
    min = afm->kern_pairs;
    max = min + afm->num_pairs - 1;

    while ( min <= max )
    {
      unsigned long midi;


      mid  = min + ( max - min ) / 2;
      midi = KERN_INDEX( mid->glyph1, mid->glyph2 );

      if ( midi == index )
      {
        *kerning = mid->kerning;
        break;
      }

      if ( midi < index )
        min = mid + 1;
      else
        max = mid - 1;
    }
  }

  if ( afm->num_tracks )
  {
    T1_Track_Kern*  track;

    if ( useTight )
       track = &afm->track_kern[0];
    else
       track = &afm->track_kern[afm->num_tracks - 1];

    if ( pointsize < track->minptsize )
      kerning->x += track->minkern;
    else if ( pointsize > track->maxptsize )
      kerning->x += track->maxkern;
    else
    {
      double kern = track->maxkern - track->minkern;
      kern *= ( pointsize - track->minptsize );
      kern /= ( track->maxptsize - track->minptsize );
      kern += track->minkern;
      kerning->x += kern;
    }
  }
}
