/*--------------------------------------------------------------------------
  ----- File:        t1base.c 
  ----- Author:      Rainer Menzner (Rainer.Menzner@web.de)
  ----- Date:        2001-10-03
  ----- Description: This file is part of the t1-library. It contains basic
                     routines to initialize the data structures used
		     by the t1-library.
  ----- Copyright:   t1lib is copyrighted (c) Rainer Menzner, 1996-2001. 
                     As of version 0.5, t1lib is distributed under the
		     GNU General Public Library Lincense. The
		     conditions can be found in the files LICENSE and
		     LGPL, which should reside in the toplevel
		     directory of the distribution.  Please note that 
		     there are parts of t1lib that are subject to
		     other licenses:
		     The parseAFM-package is copyrighted by Adobe Systems
		     Inc.
		     The type1 rasterizer is copyrighted by IBM and the
		     X11-consortium.
  ----- Warranties:  Of course, there's NO WARRANTY OF ANY KIND :-)
  ----- Credits:     I want to thank IBM and the X11-consortium for making
                     their rasterizer freely available.
		     Also thanks to Piet Tutelaers for his ps2pk, from
		     which I took the rasterizer sources in a format
		     independ from X11.
                     Thanks to all people who make free software living!
--------------------------------------------------------------------------*/


#define T1BASE_C


#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#if defined(_MSC_VER)
# include <io.h>
# include <sys/types.h>
# include <sys/stat.h>
#else
# include <unistd.h>
#endif
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>

#include "../type1/ffilest.h" 
#include "../type1/types.h"
#include "parseAFM.h" 
#include "../type1/objects.h"
#include "../type1/spaces.h"
#include "../type1/util.h"
#include "../type1/fontfcn.h"
#include "../type1/regions.h"


#include "sysconf.h"
#include "t1base.h"
#include "t1types.h"
#include "t1global.h"
#include "t1env.h"
#include "t1delete.h"


static int T1_pad=0;


/* This function is to be called by the user to initialize
   the font mechanism */
void *T1_InitLib( int log)
{
  int result;
  int i;
  
  char *usershome=NULL;
  char *logfilepath=NULL;
  char *envlogreq=NULL;
  int  usrforcelog=0;
  
  
  
  /* Reset T1_errno */
  T1_errno=0;
  
  /* Assign pointer to global struct and set entry padding: */
  pFontBase=&FontBase;
  if (T1_pad)
    pFontBase->bitmap_pad=T1_pad;
  else
    pFontBase->bitmap_pad=T1GLYPH_PAD;

  pFontBase->pFontArray = NULL;
  pFontBase->t1lib_flags=0;
  /* Check for AA-caching */
  if ((log & T1_AA_CACHING)){
    pFontBase->t1lib_flags |= T1_AA_CACHING;
  }
  /* Check for AFM disable */
  if ((log & T1_NO_AFM)) {
    pFontBase->t1lib_flags |= T1_NO_AFM;
  }

  /* Check environment variable ENV_LOG_STRING. By this means, a user may
     generate a log file even if at compile time log file creation has
     been suppressed. Of course, if the loglevel is reduced after
     initialization by the programmer, this environment variable takes
     no effect! */
  if ((envlogreq=getenv(ENV_LOG_STRING))!=NULL) {
    if (strcmp( envlogreq, "logDebug")==0)
      T1_SetLogLevel( T1LOG_DEBUG);
    else if (strcmp( envlogreq, "logStatistic")==0)
      T1_SetLogLevel( T1LOG_STATISTIC);
    else if (strcmp( envlogreq, "logWarning")==0)
      T1_SetLogLevel( T1LOG_WARNING);
    else if (strcmp( envlogreq, "logError")==0)
      T1_SetLogLevel( T1LOG_ERROR);
    usrforcelog=1;
  }
  
  /* Open log-file: */
  t1lib_log_file=NULL;
  if ((log & LOGFILE) || (usrforcelog!=0)) {
    pFontBase->t1lib_flags |= LOGFILE;
    /* Try first opening in current directory: */
    if ((t1lib_log_file=fopen( T1_LOG_FILE, "wb"))==NULL) {
      if ((usershome=getenv("HOME"))!=NULL) {
	logfilepath=(char *)malloc((strlen(usershome) +
				    strlen(T1_LOG_FILE) + 2
				    ) * sizeof(char));
	strcpy( logfilepath, usershome);
	strcat( logfilepath, DIRECTORY_SEP);
	strcat( logfilepath, T1_LOG_FILE);
	if ((t1lib_log_file=fopen( logfilepath, "wb"))==NULL){
	  t1lib_log_file=stderr;
	}
	free( logfilepath);
      }
      else {
	t1lib_log_file=stderr;
      }
    }
    if (t1lib_log_file==stderr) {
      T1_PrintLog( "T1_InitLib()", "Unable to open a logfile, using stderr",
		   T1LOG_ERROR);
    }
  }
  
  T1_PrintLog( "T1_InitLib()", "Initialization started",
	       T1LOG_STATISTIC);
  /* Check for representation of data in memory: */
  if ((pFontBase->endian=T1_CheckEndian())){
    T1_PrintLog( "T1_InitLib()", "Using Big Endian data presentation (MSBFirst)",
		 T1LOG_DEBUG);
    pFontBase->endian=1;
  }
  else{
    T1_PrintLog( "T1_InitLib()", "Using Little Endian data presentation (LSBFirst)",
		 T1LOG_DEBUG);
    pFontBase->endian=0;
  }

  /* Save version identifier */
  sprintf( err_warn_msg_buf, "Version Identifier: %s", T1LIB_IDENT);
  T1_PrintLog( "T1_InitLib()", err_warn_msg_buf,
	       T1LOG_DEBUG);
  /* Save how t1lib is initialized */
  sprintf( err_warn_msg_buf, "Initialization flags: 0x%X", log);
  T1_PrintLog( "T1_InitLib()", err_warn_msg_buf,
	       T1LOG_DEBUG);
  /* Save padding value in log file */
  sprintf( err_warn_msg_buf, "Glyphs are padded to %d bits",
	   pFontBase->bitmap_pad);
  T1_PrintLog( "T1_InitLib()", err_warn_msg_buf,
	       T1LOG_DEBUG);
#ifdef __CHAR_UNSIGNED__
  T1_PrintLog( "T1_InitLib()", "System-Info: char is unsigned",
	       T1LOG_DEBUG);
#else
  T1_PrintLog( "T1_InitLib()", "System-Info: char is signed",
	       T1LOG_DEBUG);
#endif
  sprintf( err_warn_msg_buf, "System-Info: sizeof(char):         %d",
	   SIZEOF_CHAR);
  T1_PrintLog( "T1_InitLib()", err_warn_msg_buf, T1LOG_DEBUG);
  sprintf( err_warn_msg_buf, "System-Info: sizeof(short):        %d",
	   SIZEOF_SHORT);
  T1_PrintLog( "T1_InitLib()", err_warn_msg_buf, T1LOG_DEBUG);
  sprintf( err_warn_msg_buf, "System-Info: sizeof(int):          %d",
	   SIZEOF_INT);
  T1_PrintLog( "T1_InitLib()", err_warn_msg_buf, T1LOG_DEBUG);
  sprintf( err_warn_msg_buf, "System-Info: sizeof(long):         %d",
	   SIZEOF_LONG);
  T1_PrintLog( "T1_InitLib()", err_warn_msg_buf, T1LOG_DEBUG);
  sprintf( err_warn_msg_buf, "System-Info: sizeof(long long):    %d",
	   SIZEOF_LONG_LONG);
  T1_PrintLog( "T1_InitLib()", err_warn_msg_buf, T1LOG_DEBUG);
  sprintf( err_warn_msg_buf, "System-Info: sizeof(float):        %d",
	   SIZEOF_FLOAT);
  T1_PrintLog( "T1_InitLib()", err_warn_msg_buf, T1LOG_DEBUG);
  sprintf( err_warn_msg_buf, "System-Info: sizeof(double):       %d",
	   SIZEOF_DOUBLE);
  T1_PrintLog( "T1_InitLib()", err_warn_msg_buf, T1LOG_DEBUG);
  sprintf( err_warn_msg_buf, "System-Info: sizeof(long double):  %d",
	   SIZEOF_LONG_DOUBLE);
  T1_PrintLog( "T1_InitLib()", err_warn_msg_buf, T1LOG_DEBUG);
  sprintf( err_warn_msg_buf, "System-Info: sizeof(void *):       %d",
	   SIZEOF_VOID_P);
  T1_PrintLog( "T1_InitLib()", err_warn_msg_buf, T1LOG_DEBUG);
  
  intT1_SetupDefaultSearchPaths();
  if (log & IGNORE_CONFIGFILE) {
    pFontBase->t1lib_flags |= IGNORE_CONFIGFILE;
    T1_PrintLog( "T1_InitLib()", "Skipping configuration file search!",
		 T1LOG_STATISTIC);
  }
  else {
    if ((result=intT1_ScanConfigFile())==0)
      T1_PrintLog( "T1_InitLib()", "Warning t1lib configuration file not found!",
		   T1LOG_WARNING);
  }
  

  /* Set the default encoding to the fonts' internal encoding */
  pFontBase->default_enc=NULL;
  
  /* Initialize the no_fonts... values */
  pFontBase->no_fonts=0;
  pFontBase->no_fonts_ini=pFontBase->no_fonts;
  pFontBase->no_fonts_limit=pFontBase->no_fonts;

  
  /* Check whether to read font database */
  if ((log & IGNORE_FONTDATABASE)){
    pFontBase->t1lib_flags |= IGNORE_FONTDATABASE;

    T1_Up=1;         /* System has been initialized ! */
    T1_PrintLog( "T1_InitLib()", "Initialization successfully finished (Database empty)",
		 T1LOG_STATISTIC);
    
    return((void *) pFontBase);
  }

  result=0;
  /* Read fontdatabase(s) */
  i=0;
  while (T1_FDB_ptr[i]!=NULL) {
    if ((result=intT1_scanFontDBase(T1_FDB_ptr[i]))==-1){
      T1_PrintLog( "T1_InitLib()", "Fatal error scanning Font Database File %s",
		   T1LOG_WARNING, T1_FDB_ptr[i]);
    }
    if (result>-1)
      pFontBase->no_fonts+=result;
    i++;
    
  }
  if (result == 0){
    T1_PrintLog( "T1_InitLib()", "No fonts from Font Database File(s) found (T1_errno=%d)",
		 T1LOG_ERROR, T1_errno);
    return(NULL);
  }

  /* Initialize the no_fonts... values */
  pFontBase->no_fonts_ini=pFontBase->no_fonts;
  pFontBase->no_fonts_limit=pFontBase->no_fonts;

  T1_Up=1;         /* System has been initialized ! */
  T1_PrintLog( "T1_InitLib()", "Initialization successfully finished",
	     T1LOG_STATISTIC);

  return((void *) pFontBase);
}



/* intT1_scanFontDBase():
   - opens the file with the font definitions,
   - reads the number of fonts defined and saves this in FontBase,
   - allocates memory for all the filenames of the Type1 files
   - tests for .pfa und .pfb files and saves the name found
   - initializes an array that allows to acces these names by an
     index number, the font_ID
   - returns -1 on fatal error and the number of fonts located
     successfullly
   */
int intT1_scanFontDBase( char *filename)
{
  int fd;
  int filesize, i, j, k, m;
  int found=0, located=0;
  char *filebuffer;
  int nofonts=0;
  FONTPRIVATE* fontarrayP=NULL;
  
  
#ifndef O_BINARY
#  define O_BINARY 0x0
#endif

  if ((fd=open( filename, O_RDONLY | O_BINARY))<3){
    T1_PrintLog( "intT1_scanFontDBase()", "Font Database File %s not found!",
		 T1LOG_WARNING, filename);
    T1_errno=T1ERR_FILE_OPEN_ERR;
    return(-1);
  }

  /* Get the file size */
  filesize=lseek( fd, 0, 2);
  /* Reset fileposition to start */
  lseek (fd, 0, 0); 

  if ((filebuffer=(char *)malloc(filesize*sizeof(char)
				 )) == NULL){
    T1_PrintLog(  "intT1_scanFontDBase()",
		  "Couldn't allocate memory for loading font database file %s",
		  T1LOG_ERROR, filename);
    T1_errno=T1ERR_ALLOC_MEM;
    return(-1);
  }
  
  i=read( fd, filebuffer, filesize);
  close(fd);   /* Close font database file */
   
  i=j=m=0;
  
  while (i<filesize) {
    if (filebuffer[i]=='\n'){ /* We are at the end of line */
      if (j==0) {  /* Read the first line as the number of fonts */
	filebuffer[i]=0;
	sscanf( &filebuffer[0], "%d", &nofonts);
	filebuffer[i]='\n';  /* Because it gives a better feeling */
	/* (Re)Allocate memory for 'no_fonts' structures: */ 
	if ((FontBase.pFontArray=(FONTPRIVATE *)
	     realloc( FontBase.pFontArray, (FontBase.no_fonts+nofonts)*sizeof(FONTPRIVATE))) == NULL) {
	  T1_PrintLog( "inT1_scanFontDBase()",
		       "Failed to allocate memory for FONTPRIVATE-area while scanning %s",
		       T1LOG_ERROR, filename);
	  T1_errno=T1ERR_ALLOC_MEM;
	  return(-1);
	}
	/* setup pointer to newly allocated area and do a reset */
	fontarrayP=&(FontBase.pFontArray[FontBase.no_fonts]);
	memset(fontarrayP, 0, nofonts*sizeof(FONTPRIVATE));
	located=1; /* In  order to increment m */
      }
      else {       /* We are in the second or higher line */
	k=i;
	while (isspace((int)filebuffer[k])){
	  k--;
	}
	/* We are at the last printable character of a line; let's 
	   step back (over the [afm/sfm]) to the period and replace it
	   by an ASCII 0 */
	while ((filebuffer[k]!='.') && (!isspace((int)filebuffer[k])) ){
	  k--;
	}
	if (filebuffer[k]=='.'){ /* We have a name terminated with . */
	  filebuffer[k]=0; /* termination for string reading */
	  while (!isspace((int)filebuffer[k])){
	    k--;
	  }
	}
	else { /* The filename was without . and / or the first on the line */ 
	  ;
	}
	sscanf( &(filebuffer[k+1]), "%s", &(linebuf[0]));
	/* We print error string before testing because after the call
	   to test_for_t1_file() filename is substituted by an emty
	   string if the file was not found: */
	sprintf( err_warn_msg_buf, "Type 1 Font file %s.[pfa/pfb] not found (FontID=%d, SearchPath=%s)",
		 linebuf, m-1, T1_GetFileSearchPath(T1_PFAB_PATH));
	if ((test_for_t1_file( &linebuf[0]))){
	  T1_PrintLog( "intT1_scanFontDBase()", err_warn_msg_buf, T1LOG_WARNING);
	  located=0;
	}
	else{
	  /* linebuf contains now the valid Type1 filename; let's
	     now copy this string into the appropriate place in the
	     FONTPRIVATE-struct: */
	  found++;
	  located=1;
	  if ((fontarrayP[m-1].pFontFileName=(char *)
	       calloc( strlen( &linebuf[0])+1, sizeof(char))) == NULL){
	    T1_PrintLog( "intT1_scanFontDBase()",
			 "Failed to allocate memory for Filename %s (FontID=%d)",
			 T1LOG_ERROR, &linebuf[0], m-1);
	    T1_errno=T1ERR_ALLOC_MEM;
	    return(-1);
	  }
	  strcpy( fontarrayP[m-1].pFontFileName, &linebuf[0]);
	}
      }
      j++; /* Advance line counter */
      if ((located))
	m++;
    }
    if (j>nofonts) /* to ignore especially white space at end */
      break;
    i++;   /* Step further in file position */
  }
  /* Return the memory for file reading */
  free(filebuffer); 
  
  return( found);
}


/* T1_CloseLib(): Close the library and free all associated memory */
int T1_CloseLib( void)
{

  int i, j, error=0;

  if (T1_Up){
    for (i=pFontBase->no_fonts; i; i--){
      /* Free filename only if not NULL and if the font is physical!
	 Do it before removing the font since the physical information
	 is no more available afterwards. If necessary, an explicitly
	 specified AFM filename is also freed.
	 */
      if ((pFontBase->pFontArray[i-1].pFontFileName!=NULL)
	  && (pFontBase->pFontArray[i-1].physical==1)){
	free( pFontBase->pFontArray[i-1].pFontFileName);
	pFontBase->pFontArray[i-1].pFontFileName=NULL;
	if (pFontBase->pFontArray[i-1].pAfmFileName!=NULL){
	  free( pFontBase->pFontArray[i-1].pAfmFileName);
	  pFontBase->pFontArray[i-1].pAfmFileName=NULL;
	}
      }
      
      /* Now, remove font: */
      if ((j=T1_DeleteFont( i-1))){
	error=1;
	sprintf( err_warn_msg_buf, "T1_DeleteFont() returned %d for Font %d",
		 j, i-1);
	T1_PrintLog( "T1_CloseLib()", err_warn_msg_buf, T1LOG_ERROR);
      }
    }
    /* Free the FONTPRIVATE area */
    if (pFontBase->pFontArray)
      free( pFontBase->pFontArray);
    else
      error=1;

    /* Free search paths */
    intT1_FreeSearchPaths();

    /* Reset the flags */
    pFontBase->t1lib_flags=0;
    
    /* Indicate Library is no longer initialized */
    pFontBase=NULL;
    T1_Up=0;
    T1_PrintLog( "T1_CloseLib()", "Library closed", T1LOG_STATISTIC);
    if ((t1lib_log_file!=NULL) && (t1lib_log_file!=stderr))
      fclose(t1lib_log_file);
    t1lib_log_file=NULL;
  }
  
  return( error);
}


/* T1_AddFont(): Add a new fontfile to the fontdatabase.
   Return values: >0: Assigned FontID
                  -1: Fontfile not found
		  -2: Error allocating memory for FONTPRIVATE-area
		  -3: No memory for saving font filename
		  */
int T1_AddFont( char *fontfilename)
{
  
  char *FullName;
  FONTPRIVATE *save_ptr;
  int i;
  int new_ID;
  
  
  if (fontfilename==NULL){
    T1_errno=T1ERR_INVALID_PARAMETER;
    return(-1);
  }
  
  /* Check for existence of fontfile */
  if ((FullName=intT1_Env_GetCompletePath(fontfilename,T1_PFAB_ptr))==NULL) {
    T1_errno=T1ERR_FILE_OPEN_ERR;
    return(-1);
  }
  free(FullName);

  /* Check if free space for a new FONTPRIVATE is available; if not,
     realloc memory some amount larger */
  save_ptr=pFontBase->pFontArray;
  if (pFontBase->no_fonts==pFontBase->no_fonts_limit){
    if (pFontBase->pFontArray == NULL) {
      /* In case this is the first font */
      pFontBase->pFontArray=(FONTPRIVATE *)calloc(pFontBase->no_fonts_limit 
						  + ADVANCE_FONTPRIVATE,
						  sizeof(FONTPRIVATE));
    }
    else {
      /* We already have some fonts */
      pFontBase->pFontArray=(FONTPRIVATE *)realloc(pFontBase->pFontArray,
						   (pFontBase->no_fonts_limit
						    + ADVANCE_FONTPRIVATE)
						   * sizeof(FONTPRIVATE));
      if (pFontBase->pFontArray==NULL){
	/* Restore pointer */
	pFontBase->pFontArray=save_ptr;
	T1_errno=T1ERR_ALLOC_MEM;
	return(-2); /* No memory available */
      }
    }
    pFontBase->no_fonts_limit += ADVANCE_FONTPRIVATE;
    /* First, initialize newly allocated to be not used */
    for ( i=pFontBase->no_fonts;
	  i<pFontBase->no_fonts+ADVANCE_FONTPRIVATE; i++){
      pFontBase->pFontArray[i].pFontFileName=NULL;
      pFontBase->pFontArray[i].pAfmFileName=NULL;
      pFontBase->pFontArray[i].pAFMData=NULL;
      pFontBase->pFontArray[i].pType1Data=NULL;
      pFontBase->pFontArray[i].pEncMap=NULL;
      pFontBase->pFontArray[i].pKernMap=NULL;
      pFontBase->pFontArray[i].pFontEnc=NULL;
      pFontBase->pFontArray[i].pFontSizeDeps=NULL;
      pFontBase->pFontArray[i].vm_base=NULL;
      pFontBase->pFontArray[i].FontMatrix[0]=0.0;
      pFontBase->pFontArray[i].FontMatrix[1]=0.0;
      pFontBase->pFontArray[i].FontMatrix[2]=0.0;
      pFontBase->pFontArray[i].FontMatrix[3]=0.0;
      pFontBase->pFontArray[i].FontTransform[0]=0.0;
      pFontBase->pFontArray[i].FontTransform[1]=0.0;
      pFontBase->pFontArray[i].FontTransform[2]=0.0;
      pFontBase->pFontArray[i].FontTransform[3]=0.0;
      pFontBase->pFontArray[i].slant=0.0;
      pFontBase->pFontArray[i].extend=0.0;
      pFontBase->pFontArray[i].physical=0;
      pFontBase->pFontArray[i].refcount=0; 
      pFontBase->pFontArray[i].space_position=0; 
      pFontBase->pFontArray[i].info_flags=0; 
    }
  }
  /* no_fonts-1 was the largest allowed font ID */
  new_ID=pFontBase->no_fonts;
  pFontBase->no_fonts++;

  if ((FontBase.pFontArray[new_ID].pFontFileName=(char *)
       calloc( strlen( fontfilename)+1, sizeof(char))) == NULL){
    T1_PrintLog( "T1_AddFont()",
		 "Failed to allocate memory for Filename %s (FontID=%d)",
		 T1LOG_ERROR, fontfilename, new_ID);
    T1_errno=T1ERR_ALLOC_MEM;
    return(-3);
  }
  strcpy( FontBase.pFontArray[new_ID].pFontFileName, fontfilename);

  /* Generate logfile entry */
  sprintf( err_warn_msg_buf, "Assigned FontID %d to fontfile %s",
	   new_ID, FontBase.pFontArray[new_ID].pFontFileName);
  T1_PrintLog( "T1_AddFont()", err_warn_msg_buf,
	       T1LOG_STATISTIC);
  /* Return FontID of newly declared font */
  return( new_ID);
  
}


/* T1_PrintLog() generates entries in the log file. msg_txt is subject to scan
   conversion and ... signifies a accordingly lrge variable list. */
void T1_PrintLog( char *func_ident, char *msg_txt, int level, ...)
{
  va_list vararg;
  static char levelid[4]={ 'E', 'W', 'S', 'D'};
  time_t s_clock, *tp;
  
  if (t1lib_log_file==NULL)
    return;
  if ((level>t1lib_log_level) || (level<1)){
    return;
  }
  else{
    /* initialize argument list */
    va_start( vararg, level);
    
    tp=&s_clock;
    s_clock=time( tp);
    /*
    fprintf( t1lib_log_file, "(%c) (%.24s) %s: ",
	     levelid[level-1], ctime(&s_clock), func_ident);
	     */
    /* Don't print the time stamp */
    fprintf( t1lib_log_file, "(%c) %s: ", levelid[level-1], func_ident );
    vfprintf( t1lib_log_file, msg_txt, vararg );
    fprintf( t1lib_log_file, "\n");
    fflush( t1lib_log_file);

    /* cleanup variable list */
    va_end( vararg);
    
    return;
  }
}


/* T1_SetLogLevel(): Set the level which a message must have so
   that it is printed into the logfile. This function may be called
   before T1_InitLib(). */
void T1_SetLogLevel( int level)
{
  if ((level>0) && (level<5))
    t1lib_log_level=level;
  return;
}



/* CheckForInit(): If no initialization of font mechanism has been
   done, return -1, indicating an error. */
int CheckForInit(void)
{
  if(T1_Up)
    return(0);
  else
    return(-1);
  
}



/* CheckForFontID(): Checks the font mechanism concerning the specified
   ID. It returns:
                   0  if font belonging to FontID has not yet been loaded
		   1  if font belonging to FontID has already been loaded
		   -1 if FontID is an invalid specification or t1lib not
		      initialized
		   */
int CheckForFontID( int FontID)
{

  /* FontID is invalid */
  if ((FontID<0)||(FontID>(pFontBase->no_fonts - 1))||(T1_Up==0))
    return(-1);
  
  if (pFontBase->pFontArray[FontID].pType1Data==NULL)
    return(0);     /* has not yet been loaded */
  else
    return(1);     /* has already been loaded */
}

 


/* test_for_t1_file returns 0 if a file "name.pfa" or "name.pfb"
   was found. Else, -1 is returned. If successful, buffer contains the
   found filename string */
int test_for_t1_file( char *buffer )
{
  int i=0;
  char *FullName;
  
  /* First case: A PostScript Font ASCII File without extension
     (according to some UNIX-conventions) */
  if ((FullName=intT1_Env_GetCompletePath(buffer,T1_PFAB_ptr))!=NULL) {
    free(FullName);
    return(0);
  }

  while (buffer[i]!=0){
    i++;
  }
  buffer[i]='.';
  buffer[i+1]='p';
  buffer[i+2]='f';
  buffer[i+4]=0;

  
  /* Second case: A PostScript Font ASCII File */
  buffer[i+3]='a';
  if ((FullName=intT1_Env_GetCompletePath(buffer,T1_PFAB_ptr))!=NULL) {
    free(FullName);
    return(0);
  }
  /* Third case: A PostScript Font Binary File */
  buffer[i+3]='b';
  if ((FullName=intT1_Env_GetCompletePath(buffer,T1_PFAB_ptr))!=NULL) {
    free(FullName);
    return(0);
  }
  
  /* If we get here no file was found => Set buffer
     to an empty string and return -1 */
  
  buffer[0]=0;
  return(-1);
}


/* T1_GetFontFileName() returns a pointer to the filename of the font,
   associated with FontID. This filename does not contain a full path.
   */
char *T1_GetFontFileName( int FontID)
{

  static char filename[T1_MAXPATHLEN+1];
  
  if (CheckForInit())return(NULL);

  /* Check first for valid FontID */
  if ((FontID<0) || (FontID>FontBase.no_fonts)){
    T1_errno=T1ERR_INVALID_FONTID;
    return(NULL);
  }
  
  strcpy( filename, pFontBase->pFontArray[FontID].pFontFileName);
  
  return( filename);
  
}



/* As suggested by Nicolai Langfeldt, we make it possible to specify
   a completely independent path for the afm filename. This should
   make t1lib usable in context with using the kpathsearch-library.
   We allow setting those path´s after initialization, but before a
   font is loaded.
   returns  0:   OK
           -1:   Operation could not be performed
*/
int T1_SetAfmFileName( int FontID, char *afm_name)
{

  if (CheckForFontID(FontID)!=0){
    /* Operation may not be applied because FontID is invalid
       or font is loaded */
    T1_errno=T1ERR_INVALID_FONTID;
    return(-1);
  }
  if (afm_name==NULL) {
    T1_errno=T1ERR_INVALID_PARAMETER;
    return(-1);
  }
  if (pFontBase->pFontArray[FontID].pAfmFileName!=NULL){
    /* we first free the current name */
    free( pFontBase->pFontArray[FontID].pAfmFileName);
    pFontBase->pFontArray[FontID].pAfmFileName=NULL;
  }
  
  if ((pFontBase->pFontArray[FontID].pAfmFileName=
       (char *)malloc( (strlen(afm_name)+1)*sizeof( char)))==NULL) {
    T1_errno=T1ERR_ALLOC_MEM;
    return( -1);
  }
  strcpy( pFontBase->pFontArray[FontID].pAfmFileName, afm_name);
	  
  return(0);
  
}

		  
 
/* We have a function for querying the name. Returns a pointer
   to the string or NULL if name was not explicitly set .*/
char *T1_GetAfmFileName( int FontID)
{

  static char filename[T1_MAXPATHLEN+1];
  
  if (CheckForInit())return(NULL);

  /* Check first for valid FontID */
  if ((FontID<0) || (FontID>FontBase.no_fonts)){
    T1_errno=T1ERR_INVALID_FONTID;
    return(NULL);
  }

  if (pFontBase->pFontArray[FontID].pAfmFileName==NULL) {
    return( NULL);
  }
  
  strcpy( filename, pFontBase->pFontArray[FontID].pAfmFileName);
  return( filename);
  
}


  
/* T1_Get_no_fonts(): Return the number of declared fonts */
int  T1_Get_no_fonts(void)
{
  if (CheckForInit())return(-1);
  return(FontBase.no_fonts);
}

	

/* T1_SetDeviceResolutions( x_res, y_res): Set the device's physical
   resolution in horizontal and vertical direction, mesured in DPI
   (Dots Per Inch). This should be done before the first font is
   loaded! */
int T1_SetDeviceResolutions( float x_res, float y_res)
{

  int i;
  
  if (CheckForInit())
    ;   /* Not initialized -> no size dependent data -> OK */
  else
    /* Check if size-dependent data is existent */
    for ( i=T1_Get_no_fonts(); i; i--)
      if (pFontBase->pFontArray[i-1].pFontSizeDeps!=NULL){
	T1_errno=T1ERR_OP_NOT_PERMITTED;
	return(-1); /* There's is size dependent data for a font */
      }
  
  /* Save resolutions and calculate horizontal and vertical
     scale factors to map desired bp to device pixel */
  DeviceSpecifics.x_resolution=(float) x_res;
  DeviceSpecifics.y_resolution=(float) y_res;
  DeviceSpecifics.scale_x=(float)(((float)x_res)/BIGPOINTSPERINCH);
  DeviceSpecifics.scale_y=(float)(((float)y_res)/BIGPOINTSPERINCH);
  return(0);
}


/* T1_QueryX11Support(): Check at runtime to see if t1lib was compiled
   with X11 interface: */
int T1_QueryX11Support( void)
{
#ifndef T1LIB_NO_X11_SUPPORT
  return(1);
#else
  return(0);
#endif
}




/* int T1_CopyFont(): Copies the font associated with FontID to another
   location. The pointers to type1- , afm- and encoding data as well as
   the matrices remain completely untouched. However, size dependent data
   is not copied. The produced font is marked as a "logical" font.
   If no memory is available in the FONTPRIVATE-array, there's realloc'ed
   some more memory. The FontID which is assigned to the newly generated
   font is given as the return value, or < 0 if an error occurs. Also,
   the refcount entry of the source font is incremented by one.

   Return value -1: invalid FontID specified
                -2: source font is not a "physical" font
		-3: no memory for reallocation of FONTPRIVATEs
		-4: no memory for one of the mapping tables
		*/
int T1_CopyFont( int FontID)
{
  FONTPRIVATE *save_ptr;
  int k;
  int new_ID;
  
  
  /* Check for a valid source font */
  if (CheckForFontID(FontID)!=1){
    T1_errno=T1ERR_INVALID_FONTID;
    return(-1);
  }


  /* Check if the font in question is a "physical" font, otherwise it may
     not be copied */
  if (pFontBase->pFontArray[FontID].physical==0){
    T1_errno=T1ERR_OP_NOT_PERMITTED;
    return(-2);
  }
  
  
  /* Check if free space for a new FONTPRIVATE is available; if not,
     realloc memory some amount larger */
  save_ptr=pFontBase->pFontArray;
  if (pFontBase->no_fonts==pFontBase->no_fonts_limit){
    pFontBase->pFontArray=(FONTPRIVATE *)realloc(pFontBase->pFontArray,
						 (pFontBase->no_fonts_limit
						  + ADVANCE_FONTPRIVATE)
						 * sizeof(FONTPRIVATE));
    if (pFontBase->pFontArray==NULL){
      /* Restore pointer */
      pFontBase->pFontArray=save_ptr;
      T1_errno=T1ERR_ALLOC_MEM;
      return(-3);
    }
    /* We zero the newly allocated memory */
    if (pFontBase->pFontArray != NULL) {
      memset( pFontBase->pFontArray + pFontBase->no_fonts_limit, 0,
	      ADVANCE_FONTPRIVATE * sizeof(FONTPRIVATE));
    }
    pFontBase->no_fonts_limit += ADVANCE_FONTPRIVATE;
  }
  /* no_fonts-1 was the largest allowed font ID */
  new_ID=pFontBase->no_fonts;
  /* Copy FONTPRIVATE-structure: */
  pFontBase->pFontArray[new_ID]=pFontBase->pFontArray[FontID];
  /* (Re)Set some values explicitly, others remain untouched: */  
  pFontBase->pFontArray[new_ID].pFontSizeDeps=NULL;
  pFontBase->pFontArray[new_ID].physical=0;
  /* AFM-mapping tables are to be setup for logical fonts separately
   (if AFM data is there) */
  /* first, kerning map */
  if (pFontBase->pFontArray[new_ID].pAFMData) {
    k=pFontBase->pFontArray[new_ID].pAFMData->numOfPairs;
    if (k>0){ /* kern map exists only if kerning pairs exist! */
      if ((pFontBase->pFontArray[new_ID].pKernMap=
	   (METRICS_ENTRY *)malloc( k*sizeof( METRICS_ENTRY)))==NULL){
	sprintf( err_warn_msg_buf, "Error allocating memory for kerning map (new_ID=%d)",
		 new_ID);
	T1_PrintLog( "T1_CopyFont()", err_warn_msg_buf,
		     T1LOG_WARNING);
	T1_errno=T1ERR_ALLOC_MEM;
	return(-4);
      }
      memcpy( pFontBase->pFontArray[new_ID].pKernMap,
	      pFontBase->pFontArray[FontID].pKernMap,
	      k*sizeof( METRICS_ENTRY));
    } 
    else { /* no kerning pairs, bu AFM data present */
      pFontBase->pFontArray[new_ID].pKernMap=NULL;
    }
  } 
  else { /* AFM data not present at all */
    pFontBase->pFontArray[new_ID].pKernMap=NULL;
  }

  /* second, encoding map */
  if (pFontBase->pFontArray[FontID].pEncMap!=NULL) {
    if ((pFontBase->pFontArray[new_ID].pEncMap=
	 (int *)calloc(256,sizeof(int)))==NULL){
      sprintf( err_warn_msg_buf,
	       "Error allocating memory for encoding map (new_ID=%d)",
	       new_ID);
      T1_PrintLog( "T1_CopyFont()", err_warn_msg_buf,
		   T1LOG_WARNING);
      T1_errno=T1ERR_ALLOC_MEM;
      return(-4);
    }
    memcpy( pFontBase->pFontArray[new_ID].pEncMap,
	    pFontBase->pFontArray[FontID].pEncMap,
	    256*sizeof( int));
  }
  
  /* New font is logical --> indicate to which physical font it
     refers by setting refcount: */
  pFontBase->pFontArray[new_ID].refcount=FontID;
  
  /* Now the struct is setup; increment no_fonts by 1 because
     new_ID is a valid font specification from now on. */
  pFontBase->no_fonts++;
  /* Increment refcount in source font */
  pFontBase->pFontArray[FontID].refcount++;

  /* Generate logfile entry */
  sprintf( err_warn_msg_buf, "Assigned FontID %d to fontfile %s",
	   new_ID, FontBase.pFontArray[new_ID].pFontFileName);
  T1_PrintLog( "T1_CopyFont()", err_warn_msg_buf,
	       T1LOG_STATISTIC);
  return(new_ID);
}



/* T1_SetBitmapPad(): Set the value to which bitmap-scanlines are padded.
   This has to be done before initialization because it is a very
   rudimentary operation.
   */
int T1_SetBitmapPad( int pad)
{
  if (T1_Up){
    /* Library is initialized --> return error */
    T1_errno=T1ERR_OP_NOT_PERMITTED;
    return(-1);
  }
  else{
    switch (pad){
    case 8: 
      T1_pad=8;
      return(0);
    case 16:
      T1_pad=16;
      return(0);
#ifdef T1_AA_TYPE64
    case 32:
      T1_pad=32;
      return(0);
#endif
    default:
      T1_errno=T1ERR_INVALID_PARAMETER;
      return(-1);
    }
  }
}



/* T1_GetBitmapPad(): Read the value to which scanlines of bitmap are
   padded. This can be done before or after initialization. */
int T1_GetBitmapPad( void)
{
  if (pFontBase) /* T1lib initialized --> return value from struct */
    return( pFontBase->bitmap_pad);
  else{
    if (T1_pad) 
      return(T1_pad);  /* pad is explicitly set --> return that value */
    else
      return( T1GLYPH_PAD); /* not expl. set --> return compilation default */
  }
}



/* bin_dump(): Print a binary dump of a byte, short and
   long variable (used for debug purposes only): */
void bin_dump_c(unsigned char value, char space_flag)
{
  int i,j;
  
  for (i=0;i<=7;i++){
    if ((j=((value)>>i)&0x01))
      printf("X");
    else
      printf(".");
  }
  if (space_flag)
    printf(" ");

}

void bin_dump_s(unsigned short value, char space_flag)
{
  int i,j;

  if (T1_CheckEndian()){
    for (i=8;i<=15;i++){
      if ((j=((value)>>i)&0x01))
	printf("X");
      else
	printf(".");
    }
    for (i=0;i<=7;i++){
      if ((j=((value)>>i)&0x01))
	printf("X");
      else
	printf(".");
    }
  }
  else{
    for (i=0;i<=15;i++){
      if ((j=((value)>>i)&0x01))
	printf("X");
      else
	printf(".");
    }
  }
  if (space_flag)
    printf(" ");
  
}

void bin_dump_l(unsigned long value, char space_flag)
{
  int i,j;
  
  if (T1_CheckEndian()){
    for (i=24;i<=31;i++){
      if ((j=((value)>>i)&0x01))
	printf("X");
      else
	printf(".");
    }
    for (i=16;i<=23;i++){
      if ((j=((value)>>i)&0x01))
	printf("X");
      else
	printf(".");
    }
    for (i=8;i<=15;i++){
      if ((j=((value)>>i)&0x01))
	printf("X");
      else
	printf(".");
    }
    for (i=0;i<=7;i++){
      if ((j=((value)>>i)&0x01))
	printf("X");
      else
	printf(".");
    }
  }
  else{
    for (i=0;i<=31;i++){
      if ((j=((value)>>i)&0x01))
	printf("X");
      else
	printf(".");
    }
  }
  if (space_flag)
    printf(" ");

}



/* CheckEndian(): Checks whether the current machine is of little or big
   endian architecture. This is important for concatenating bitmaps.
   Function returns 0 if LittleEndian and 1 if BigEndian representation
   is used  on the current hardware.
   */
int T1_CheckEndian()
{
  unsigned char *charptr;

  /* Generate test value */
  unsigned short test=0x0001;

  /* Read out memory as unsigned char */
  charptr=(unsigned char *)(&test)+1;

  /* Return value will be 1 if Big- and 0 if Little Endian */
  return((int) *charptr);
  
}



/* T1_GetLibIdent(): Return the identifier string for the current version
   of t1lib */
char *T1_GetLibIdent( void)
{
  static char buf[15];

  sprintf( buf, "%s", T1LIB_IDENT);
  
  return( (char *)buf);
}



/* T1_SetRasterFlags(): Enable/Disable certain features in
   the rasterizer */
extern void T1_SetRasterFlags( int flags)
{ 
  
  T1_Type1OperatorFlags=flags;
  return;
  
}



/* T1_GetFontFileName(): returns a pointer to the complete path filename
   of the font, associated with FontID as it is in use by t1lib.
   */
char *T1_GetFontFilePath( int FontID)
{

  static char filepath[T1_MAXPATHLEN+1];
  char *FileNamePath=NULL;
  
  /* is initialzed? */
  if (CheckForInit()) {
    T1_errno=T1ERR_INVALID_FONTID;
    return(NULL);
  }
  
  /* Check first for valid FontID */
  if ((FontID<0) || (FontID>FontBase.no_fonts)){
    T1_errno=T1ERR_INVALID_FONTID;
    return(NULL);
  }

  /* lib is initialized and FontID is valid ->
     we can really expect a name */
  if ((FileNamePath=intT1_Env_GetCompletePath( pFontBase->pFontArray[FontID].pFontFileName, 
					       T1_PFAB_ptr))==NULL) { 
    T1_PrintLog( "T1_GetFontFilePath()", "Couldn't locate font file for font %d in %s", 
		 T1LOG_WARNING, FontID, T1_GetFileSearchPath(T1_PFAB_PATH)); 
    T1_errno=T1ERR_FILE_OPEN_ERR; 
    return(NULL); 
  } 
  
  strcpy( filepath, FileNamePath);
  free( FileNamePath);
  
  return( filepath);
  
}



/* We have a function for querying the name. Returns a pointer
   to the string or NULL if name was not explicitly set .*/
char *T1_GetAfmFilePath( int FontID)
{
  
  static char filepath[T1_MAXPATHLEN+1];
  char *FontFileName;
  char *AFMFilePath;
  int i, j;
  
  /* is initialized? */
  if ((CheckForInit())) {
    T1_errno=T1ERR_INVALID_FONTID;
    return(NULL);
  }
  
  /* Check first for valid FontID */
  if ((FontID<0) || (FontID>FontBase.no_fonts)){
    T1_errno=T1ERR_INVALID_FONTID;
    return(NULL);
  }

  /* Check wether AFM-file loading was suppressed on user's request */
  if ((pFontBase->t1lib_flags & T1_NO_AFM)!=0) {
    /* this is no error condition, we simply return (NULL) */
    return( NULL);
  }
  
  /* Check for explicitly associated metrics filename (via
     "T1_SetAfmFileName()"). If it exists, we return it! */
  if (pFontBase->pFontArray[FontID].pAfmFileName!=NULL) {
    strcpy( filepath, pFontBase->pFontArray[FontID].pAfmFileName);
    sprintf( err_warn_msg_buf, "Returning explicitly specified path %s for Font %d",
	     filepath, FontID);
    T1_PrintLog( "T1_GetAfmFilePath()", err_warn_msg_buf, T1LOG_DEBUG);
    return( filepath);
  }
  
  /* we have the usual case that the name of the metrics file has to be
     deduced from the font file name */
  FontFileName=T1_GetFontFileName( FontID);
  i=strlen(FontFileName);
  j=i;
  strcpy( filepath, FontFileName);
  while ( filepath[i] != '.'){
    if (i==0) break;
    else i--;
  }
  if (i==0){
    /* We have a filename without extension -> append extension */
    filepath[j]='.';
    filepath[j+1]='a'; 
    filepath[j+2]='f'; 
    filepath[j+3]='m'; 
    filepath[j+4]='\0'; 
  }
  else{
    /* we found a '.' -> replace extension */
    filepath[i+1]='a';
    filepath[i+2]='f';
    filepath[i+3]='m';
    filepath[i+4]='\0';
  }
  /* Get full path of the afm file (The case of a full path name
     name specification is valid */
  if ((AFMFilePath=intT1_Env_GetCompletePath( filepath, T1_AFM_ptr)) == NULL) {
    return NULL;
  }
  strcpy( filepath, AFMFilePath);
  free( AFMFilePath);
  
  return( filepath);
  
}
