/***************************************************************************/

/*                                                                         */

/*  t1afm.c                                                                */

/*                                                                         */

/*    AFM support for Type 1 fonts (body).                                 */

/*                                                                         */

/*  Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,   */

/*            2010 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.                                        */

/*                                                                         */

/***************************************************************************/





#include <ft2build.h>

#include "t1afm.h"

#include "t1errors.h"

#include FT_INTERNAL_STREAM_H

#include FT_INTERNAL_POSTSCRIPT_AUX_H





  /*************************************************************************/

  /*                                                                       */

  /* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */

  /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */

  /* messages during execution.                                            */

  /*                                                                       */

#undef  FT_COMPONENT

#define FT_COMPONENT  trace_t1afm





  FT_LOCAL_DEF( void )

  T1_Done_Metrics( FT_Memory     memory,

                   AFM_FontInfo  fi )

  {

    FT_FREE( fi->KernPairs );

    fi->NumKernPair = 0;



    FT_FREE( fi->TrackKerns );

    fi->NumTrackKern = 0;



    FT_FREE( fi );

  }





  /* read a glyph name and return the equivalent glyph index */

  static FT_Int

  t1_get_index( const char*  name,

                FT_Offset    len,

                void*        user_data )

  {

    T1_Font  type1 = (T1_Font)user_data;

    FT_Int   n;





    /* PS string/name length must be < 16-bit */

    if ( len > 0xFFFFU )

      return 0;



    for ( n = 0; n < type1->num_glyphs; n++ )

    {

      char*  gname = (char*)type1->glyph_names[n];





      if ( gname && gname[0] == name[0]        &&

           ft_strlen( gname ) == len           &&

           ft_strncmp( gname, name, len ) == 0 )

        return n;

    }



    return 0;

  }





#undef  KERN_INDEX

#define KERN_INDEX( g1, g2 )  ( ( (FT_ULong)(g1) << 16 ) | (g2) )





  /* compare two kerning pairs */

  FT_CALLBACK_DEF( int )

  compare_kern_pairs( const void*  a,

                      const void*  b )

  {

    AFM_KernPair  pair1 = (AFM_KernPair)a;

    AFM_KernPair  pair2 = (AFM_KernPair)b;



    FT_ULong  index1 = KERN_INDEX( pair1->index1, pair1->index2 );

    FT_ULong  index2 = KERN_INDEX( pair2->index1, pair2->index2 );





    if ( index1 > index2 )

      return 1;

    else if ( index1 < index2 )

      return -1;

    else

      return 0;

  }





  /* parse a PFM file -- for now, only read the kerning pairs */

  static FT_Error

  T1_Read_PFM( FT_Face       t1_face,

               FT_Stream     stream,

               AFM_FontInfo  fi )

  {

    FT_Error      error = T1_Err_Ok;

    FT_Memory     memory = stream->memory;

    FT_Byte*      start;

    FT_Byte*      limit;

    FT_Byte*      p;

    AFM_KernPair  kp;

    FT_Int        width_table_length;

    FT_CharMap    oldcharmap;

    FT_CharMap    charmap;

    FT_Int        n;





    start = (FT_Byte*)stream->cursor;

    limit = (FT_Byte*)stream->limit;

    p     = start;



    /* Figure out how long the width table is.          */

    /* This info is a little-endian short at offset 99. */

    p = start + 99;

    if ( p + 2 > limit )

    {

      error = T1_Err_Unknown_File_Format;

      goto Exit;

    }

    width_table_length = FT_PEEK_USHORT_LE( p );



    p += 18 + width_table_length;

    if ( p + 0x12 > limit || FT_PEEK_USHORT_LE( p ) < 0x12 )

      /* extension table is probably optional */

      goto Exit;



    /* Kerning offset is 14 bytes from start of extensions table. */

    p += 14;

    p = start + FT_PEEK_ULONG_LE( p );



    if ( p == start )

      /* zero offset means no table */

      goto Exit;



    if ( p + 2 > limit )

    {

      error = T1_Err_Unknown_File_Format;

      goto Exit;

    }



    fi->NumKernPair = FT_PEEK_USHORT_LE( p );

    p += 2;

    if ( p + 4 * fi->NumKernPair > limit )

    {

      error = T1_Err_Unknown_File_Format;

      goto Exit;

    }



    /* Actually, kerning pairs are simply optional! */

    if ( fi->NumKernPair == 0 )

      goto Exit;



    /* allocate the pairs */

    if ( FT_QNEW_ARRAY( fi->KernPairs, fi->NumKernPair ) )

      goto Exit;



    /* now, read each kern pair */

    kp             = fi->KernPairs;

    limit          = p + 4 * fi->NumKernPair;



    /* PFM kerning data are stored by encoding rather than glyph index, */

    /* so find the PostScript charmap of this font and install it       */

    /* temporarily.  If we find no PostScript charmap, then just use    */

    /* the default and hope it is the right one.                        */

    oldcharmap = t1_face->charmap;

    charmap    = NULL;



    for ( n = 0; n < t1_face->num_charmaps; n++ )

    {

      charmap = t1_face->charmaps[n];

      /* check against PostScript pseudo platform */

      if ( charmap->platform_id == 7 )

      {

        error = FT_Set_Charmap( t1_face, charmap );

        if ( error )

          goto Exit;

        break;

      }

    }



    /* Kerning info is stored as:             */

    /*                                        */

    /*   encoding of first glyph (1 byte)     */

    /*   encoding of second glyph (1 byte)    */

    /*   offset (little-endian short)         */

    for ( ; p < limit ; p += 4 )

    {

      kp->index1 = FT_Get_Char_Index( t1_face, p[0] );

      kp->index2 = FT_Get_Char_Index( t1_face, p[1] );



      kp->x = (FT_Int)FT_PEEK_SHORT_LE(p + 2);

      kp->y = 0;



      kp++;

    }



    if ( oldcharmap != NULL )

      error = FT_Set_Charmap( t1_face, oldcharmap );

    if ( error )

      goto Exit;



    /* now, sort the kern pairs according to their glyph indices */

    ft_qsort( fi->KernPairs, fi->NumKernPair, sizeof ( AFM_KernPairRec ),

              compare_kern_pairs );



  Exit:

    if ( error )

    {

      FT_FREE( fi->KernPairs );

      fi->NumKernPair = 0;

    }



    return error;

  }





  /* parse a metrics file -- either AFM or PFM depending on what */

  /* it turns out to be                                          */

  FT_LOCAL_DEF( FT_Error )

  T1_Read_Metrics( FT_Face    t1_face,

                   FT_Stream  stream )

  {

    PSAux_Service  psaux;

    FT_Memory      memory  = stream->memory;

    AFM_ParserRec  parser;

    AFM_FontInfo   fi      = NULL;

    FT_Error       error   = T1_Err_Unknown_File_Format;

    T1_Font        t1_font = &( (T1_Face)t1_face )->type1;





    if ( FT_NEW( fi )                   ||

         FT_FRAME_ENTER( stream->size ) )

      goto Exit;



    fi->FontBBox  = t1_font->font_bbox;

    fi->Ascender  = t1_font->font_bbox.yMax;

    fi->Descender = t1_font->font_bbox.yMin;



    psaux = (PSAux_Service)( (T1_Face)t1_face )->psaux;

    if ( psaux && psaux->afm_parser_funcs )

    {

      error = psaux->afm_parser_funcs->init( &parser,

                                             stream->memory,

                                             stream->cursor,

                                             stream->limit );



      if ( !error )

      {

        parser.FontInfo  = fi;

        parser.get_index = t1_get_index;

        parser.user_data = t1_font;



        error = psaux->afm_parser_funcs->parse( &parser );

        psaux->afm_parser_funcs->done( &parser );

      }

    }



    if ( error == T1_Err_Unknown_File_Format )

    {

      FT_Byte*  start = stream->cursor;





      /* MS Windows allows versions up to 0x3FF without complaining */

      if ( stream->size > 6                              &&

           start[1] < 4                                  &&

           FT_PEEK_ULONG_LE( start + 2 ) == stream->size )

        error = T1_Read_PFM( t1_face, stream, fi );

    }



    if ( !error )

    {

      t1_font->font_bbox = fi->FontBBox;



      t1_face->bbox.xMin =   fi->FontBBox.xMin            >> 16;

      t1_face->bbox.yMin =   fi->FontBBox.yMin            >> 16;

      /* no `U' suffix here to 0xFFFF! */

      t1_face->bbox.xMax = ( fi->FontBBox.xMax + 0xFFFF ) >> 16;

      t1_face->bbox.yMax = ( fi->FontBBox.yMax + 0xFFFF ) >> 16;



      /* no `U' suffix here to 0x8000! */

      t1_face->ascender  = (FT_Short)( ( fi->Ascender  + 0x8000 ) >> 16 );

      t1_face->descender = (FT_Short)( ( fi->Descender + 0x8000 ) >> 16 );



      if ( fi->NumKernPair )

      {

        t1_face->face_flags |= FT_FACE_FLAG_KERNING;

        ( (T1_Face)t1_face )->afm_data = fi;

        fi = NULL;

      }

    }



    FT_FRAME_EXIT();



  Exit:

    if ( fi != NULL )

      T1_Done_Metrics( memory, fi );



    return error;

  }





  /* find the kerning for a given glyph pair */

  FT_LOCAL_DEF( void )

  T1_Get_Kerning( AFM_FontInfo  fi,

                  FT_UInt       glyph1,

                  FT_UInt       glyph2,

                  FT_Vector*    kerning )

  {

    AFM_KernPair  min, mid, max;

    FT_ULong      idx = KERN_INDEX( glyph1, glyph2 );





    /* simple binary search */

    min = fi->KernPairs;

    max = min + fi->NumKernPair - 1;



    while ( min <= max )

    {

      FT_ULong  midi;





      mid  = min + ( max - min ) / 2;

      midi = KERN_INDEX( mid->index1, mid->index2 );



      if ( midi == idx )

      {

        kerning->x = mid->x;

        kerning->y = mid->y;



        return;

      }



      if ( midi < idx )

        min = mid + 1;

      else

        max = mid - 1;

    }



    kerning->x = 0;

    kerning->y = 0;

  }





  FT_LOCAL_DEF( FT_Error )

  T1_Get_Track_Kerning( FT_Face    face,

                        FT_Fixed   ptsize,

                        FT_Int     degree,

                        FT_Fixed*  kerning )

  {

    AFM_FontInfo  fi = (AFM_FontInfo)( (T1_Face)face )->afm_data;

    FT_Int        i;





    if ( !fi )

      return T1_Err_Invalid_Argument;



    for ( i = 0; i < fi->NumTrackKern; i++ )

    {

      AFM_TrackKern  tk = fi->TrackKerns + i;





      if ( tk->degree != degree )

        continue;



      if ( ptsize < tk->min_ptsize )

        *kerning = tk->min_kern;

      else if ( ptsize > tk->max_ptsize )

        *kerning = tk->max_kern;

      else

      {

        *kerning = FT_MulDiv( ptsize - tk->min_ptsize,

                              tk->max_kern - tk->min_kern,

                              tk->max_ptsize - tk->min_ptsize ) +

                   tk->min_kern;

      }

    }



    return T1_Err_Ok;

  }





/* END */

