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

/*                                                                         */

/*  ttpload.c                                                              */

/*                                                                         */

/*    TrueType-specific tables loader (body).                              */

/*                                                                         */

/*  Copyright 1996-2001, 2002, 2004, 2005, 2006, 2007, 2008, 2009 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 FT_INTERNAL_DEBUG_H

#include FT_INTERNAL_OBJECTS_H

#include FT_INTERNAL_STREAM_H

#include FT_TRUETYPE_TAGS_H



#include "ttpload.h"



#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT

#include "ttgxvar.h"

#endif



#include "tterrors.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_ttpload





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

  /*                                                                       */

  /* <Function>                                                            */

  /*    tt_face_load_loca                                                  */

  /*                                                                       */

  /* <Description>                                                         */

  /*    Load the locations table.                                          */

  /*                                                                       */

  /* <InOut>                                                               */

  /*    face   :: A handle to the target face object.                      */

  /*                                                                       */

  /* <Input>                                                               */

  /*    stream :: The input stream.                                        */

  /*                                                                       */

  /* <Return>                                                              */

  /*    FreeType error code.  0 means success.                             */

  /*                                                                       */

  FT_LOCAL_DEF( FT_Error )

  tt_face_load_loca( TT_Face    face,

                     FT_Stream  stream )

  {

    FT_Error  error;

    FT_ULong  table_len;

    FT_Int    shift;





    /* we need the size of the `glyf' table for malformed `loca' tables */

    error = face->goto_table( face, TTAG_glyf, stream, &face->glyf_len );



    /* it is possible that a font doesn't have a glyf table at all */

    /* or its size is zero                                         */

    if ( error == TT_Err_Table_Missing )

      face->glyf_len = 0;

    else if ( error )

      goto Exit;



    FT_TRACE2(( "Locations " ));

    error = face->goto_table( face, TTAG_loca, stream, &table_len );

    if ( error )

    {

      error = TT_Err_Locations_Missing;

      goto Exit;

    }



    if ( face->header.Index_To_Loc_Format != 0 )

    {

      shift = 2;



      if ( table_len >= 0x40000L )

      {

        FT_TRACE2(( "table too large\n" ));

        error = TT_Err_Invalid_Table;

        goto Exit;

      }

      face->num_locations = table_len >> shift;

    }

    else

    {

      shift = 1;



      if ( table_len >= 0x20000L )

      {

        FT_TRACE2(( "table too large\n" ));

        error = TT_Err_Invalid_Table;

        goto Exit;

      }

      face->num_locations = table_len >> shift;

    }



    if ( face->num_locations != (FT_ULong)face->root.num_glyphs )

    {

      FT_TRACE2(( "glyph count mismatch!  loca: %d, maxp: %d\n",

                  face->num_locations, face->root.num_glyphs ));



      /* we only handle the case where `maxp' gives a larger value */

      if ( face->num_locations < (FT_ULong)face->root.num_glyphs )

      {

        FT_Long   new_loca_len = (FT_Long)face->root.num_glyphs << shift;



        TT_Table  entry = face->dir_tables;

        TT_Table  limit = entry + face->num_tables;



        FT_Long   pos  = FT_Stream_Pos( stream );

        FT_Long   dist = 0x7FFFFFFFL;





        /* compute the distance to next table in font file */

        for ( ; entry < limit; entry++ )

        {

          FT_Long  diff = entry->Offset - pos;





          if ( diff > 0 && diff < dist )

            dist = diff;

        }



        if ( new_loca_len <= dist )

        {

          face->num_locations = face->root.num_glyphs;

          table_len           = new_loca_len;



          FT_TRACE2(( "adjusting num_locations to %d\n",

                      face->num_locations ));

        }

      }

    }



    /*

     * Extract the frame.  We don't need to decompress it since

     * we are able to parse it directly.

     */

    if ( FT_FRAME_EXTRACT( table_len, face->glyph_locations ) )

      goto Exit;



    FT_TRACE2(( "loaded\n" ));



  Exit:

    return error;

  }





  FT_LOCAL_DEF( FT_ULong )

  tt_face_get_location( TT_Face   face,

                        FT_UInt   gindex,

                        FT_UInt  *asize )

  {

    FT_ULong  pos1, pos2;

    FT_Byte*  p;

    FT_Byte*  p_limit;





    pos1 = pos2 = 0;



    if ( gindex < face->num_locations )

    {

      if ( face->header.Index_To_Loc_Format != 0 )

      {

        p       = face->glyph_locations + gindex * 4;

        p_limit = face->glyph_locations + face->num_locations * 4;



        pos1 = FT_NEXT_ULONG( p );

        pos2 = pos1;



        if ( p + 4 <= p_limit )

          pos2 = FT_NEXT_ULONG( p );

      }

      else

      {

        p       = face->glyph_locations + gindex * 2;

        p_limit = face->glyph_locations + face->num_locations * 2;



        pos1 = FT_NEXT_USHORT( p );

        pos2 = pos1;



        if ( p + 2 <= p_limit )

          pos2 = FT_NEXT_USHORT( p );



        pos1 <<= 1;

        pos2 <<= 1;

      }

    }



    /* The `loca' table must be ordered; it refers to the length of */

    /* an entry as the difference between the current and the next  */

    /* position.  However, there do exist (malformed) fonts which   */

    /* don't obey this rule, so we are only able to provide an      */

    /* upper bound for the size.                                    */

    /*                                                              */

    /* We get (intentionally) a wrong, non-zero result in case the  */

    /* `glyf' table is missing.                                     */

    if ( pos2 >= pos1 )

      *asize = (FT_UInt)( pos2 - pos1 );

    else

      *asize = (FT_UInt)( face->glyf_len - pos1 );



    return pos1;

  }





  FT_LOCAL_DEF( void )

  tt_face_done_loca( TT_Face  face )

  {

    FT_Stream  stream = face->root.stream;





    FT_FRAME_RELEASE( face->glyph_locations );

    face->num_locations = 0;

  }







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

  /*                                                                       */

  /* <Function>                                                            */

  /*    tt_face_load_cvt                                                   */

  /*                                                                       */

  /* <Description>                                                         */

  /*    Load the control value table into a face object.                   */

  /*                                                                       */

  /* <InOut>                                                               */

  /*    face   :: A handle to the target face object.                      */

  /*                                                                       */

  /* <Input>                                                               */

  /*    stream :: A handle to the input stream.                            */

  /*                                                                       */

  /* <Return>                                                              */

  /*    FreeType error code.  0 means success.                             */

  /*                                                                       */

  FT_LOCAL_DEF( FT_Error )

  tt_face_load_cvt( TT_Face    face,

                    FT_Stream  stream )

  {

#ifdef TT_USE_BYTECODE_INTERPRETER



    FT_Error   error;

    FT_Memory  memory = stream->memory;

    FT_ULong   table_len;





    FT_TRACE2(( "CVT " ));



    error = face->goto_table( face, TTAG_cvt, stream, &table_len );

    if ( error )

    {

      FT_TRACE2(( "is missing\n" ));



      face->cvt_size = 0;

      face->cvt      = NULL;

      error          = TT_Err_Ok;



      goto Exit;

    }



    face->cvt_size = table_len / 2;



    if ( FT_NEW_ARRAY( face->cvt, face->cvt_size ) )

      goto Exit;



    if ( FT_FRAME_ENTER( face->cvt_size * 2L ) )

      goto Exit;



    {

      FT_Short*  cur   = face->cvt;

      FT_Short*  limit = cur + face->cvt_size;





      for ( ; cur <  limit; cur++ )

        *cur = FT_GET_SHORT();

    }



    FT_FRAME_EXIT();

    FT_TRACE2(( "loaded\n" ));



#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT

    if ( face->doblend )

      error = tt_face_vary_cvt( face, stream );

#endif



  Exit:

    return error;



#else /* !TT_USE_BYTECODE_INTERPRETER */



    FT_UNUSED( face   );

    FT_UNUSED( stream );



    return TT_Err_Ok;



#endif

  }





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

  /*                                                                       */

  /* <Function>                                                            */

  /*    tt_face_load_fpgm                                                  */

  /*                                                                       */

  /* <Description>                                                         */

  /*    Load the font program.                                             */

  /*                                                                       */

  /* <InOut>                                                               */

  /*    face   :: A handle to the target face object.                      */

  /*                                                                       */

  /* <Input>                                                               */

  /*    stream :: A handle to the input stream.                            */

  /*                                                                       */

  /* <Return>                                                              */

  /*    FreeType error code.  0 means success.                             */

  /*                                                                       */

  FT_LOCAL_DEF( FT_Error )

  tt_face_load_fpgm( TT_Face    face,

                     FT_Stream  stream )

  {

#ifdef TT_USE_BYTECODE_INTERPRETER



    FT_Error  error;

    FT_ULong  table_len;





    FT_TRACE2(( "Font program " ));



    /* The font program is optional */

    error = face->goto_table( face, TTAG_fpgm, stream, &table_len );

    if ( error )

    {

      face->font_program      = NULL;

      face->font_program_size = 0;

      error                   = TT_Err_Ok;



      FT_TRACE2(( "is missing\n" ));

    }

    else

    {

      face->font_program_size = table_len;

      if ( FT_FRAME_EXTRACT( table_len, face->font_program ) )

        goto Exit;



      FT_TRACE2(( "loaded, %12d bytes\n", face->font_program_size ));

    }



  Exit:

    return error;



#else /* !TT_USE_BYTECODE_INTERPRETER */



    FT_UNUSED( face   );

    FT_UNUSED( stream );



    return TT_Err_Ok;



#endif

  }





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

  /*                                                                       */

  /* <Function>                                                            */

  /*    tt_face_load_prep                                                  */

  /*                                                                       */

  /* <Description>                                                         */

  /*    Load the cvt program.                                              */

  /*                                                                       */

  /* <InOut>                                                               */

  /*    face   :: A handle to the target face object.                      */

  /*                                                                       */

  /* <Input>                                                               */

  /*    stream :: A handle to the input stream.                            */

  /*                                                                       */

  /* <Return>                                                              */

  /*    FreeType error code.  0 means success.                             */

  /*                                                                       */

  FT_LOCAL_DEF( FT_Error )

  tt_face_load_prep( TT_Face    face,

                     FT_Stream  stream )

  {

#ifdef TT_USE_BYTECODE_INTERPRETER



    FT_Error  error;

    FT_ULong  table_len;





    FT_TRACE2(( "Prep program " ));



    error = face->goto_table( face, TTAG_prep, stream, &table_len );

    if ( error )

    {

      face->cvt_program      = NULL;

      face->cvt_program_size = 0;

      error                  = TT_Err_Ok;



      FT_TRACE2(( "is missing\n" ));

    }

    else

    {

      face->cvt_program_size = table_len;

      if ( FT_FRAME_EXTRACT( table_len, face->cvt_program ) )

        goto Exit;



      FT_TRACE2(( "loaded, %12d bytes\n", face->cvt_program_size ));

    }



  Exit:

    return error;



#else /* !TT_USE_BYTECODE_INTERPRETER */



    FT_UNUSED( face   );

    FT_UNUSED( stream );



    return TT_Err_Ok;



#endif

  }





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

  /*                                                                       */

  /* <Function>                                                            */

  /*    tt_face_load_hdmx                                                  */

  /*                                                                       */

  /* <Description>                                                         */

  /*    Load the `hdmx' table into the face object.                        */

  /*                                                                       */

  /* <Input>                                                               */

  /*    face   :: A handle to the target face object.                      */

  /*                                                                       */

  /*    stream :: A handle to the input stream.                            */

  /*                                                                       */

  /* <Return>                                                              */

  /*    FreeType error code.  0 means success.                             */

  /*                                                                       */



  FT_LOCAL_DEF( FT_Error )

  tt_face_load_hdmx( TT_Face    face,

                     FT_Stream  stream )

  {

    FT_Error   error;

    FT_Memory  memory = stream->memory;

    FT_UInt    version, nn, num_records;

    FT_ULong   table_size, record_size;

    FT_Byte*   p;

    FT_Byte*   limit;





    /* this table is optional */

    error = face->goto_table( face, TTAG_hdmx, stream, &table_size );

    if ( error || table_size < 8 )

      return TT_Err_Ok;



    if ( FT_FRAME_EXTRACT( table_size, face->hdmx_table ) )

      goto Exit;



    p     = face->hdmx_table;

    limit = p + table_size;



    version     = FT_NEXT_USHORT( p );

    num_records = FT_NEXT_USHORT( p );

    record_size = FT_NEXT_ULONG( p );



    /* The maximum number of bytes in an hdmx device record is the */

    /* maximum number of glyphs + 2; this is 0xFFFF + 2; this is   */

    /* the reason why `record_size' is a long (which we read as    */

    /* unsigned long for convenience).  In practice, two bytes     */

    /* sufficient to hold the size value.                          */

    /*                                                             */

    /* There are at least two fonts, HANNOM-A and HANNOM-B version */

    /* 2.0 (2005), which get this wrong: The upper two bytes of    */

    /* the size value are set to 0xFF instead of 0x00.  We catch   */

    /* and fix this.                                               */



    if ( record_size >= 0xFFFF0000UL )

      record_size &= 0xFFFFU;



    /* The limit for `num_records' is a heuristic value. */



    if ( version != 0 || num_records > 255 || record_size > 0x10001L )

    {

      error = TT_Err_Invalid_File_Format;

      goto Fail;

    }



    if ( FT_NEW_ARRAY( face->hdmx_record_sizes, num_records ) )

      goto Fail;



    for ( nn = 0; nn < num_records; nn++ )

    {

      if ( p + record_size > limit )

        break;



      face->hdmx_record_sizes[nn] = p[0];

      p                          += record_size;

    }



    face->hdmx_record_count = nn;

    face->hdmx_table_size   = table_size;

    face->hdmx_record_size  = record_size;



  Exit:

    return error;



  Fail:

    FT_FRAME_RELEASE( face->hdmx_table );

    face->hdmx_table_size = 0;

    goto Exit;

  }





  FT_LOCAL_DEF( void )

  tt_face_free_hdmx( TT_Face  face )

  {

    FT_Stream  stream = face->root.stream;

    FT_Memory  memory = stream->memory;





    FT_FREE( face->hdmx_record_sizes );

    FT_FRAME_RELEASE( face->hdmx_table );

  }





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

  /*                                                                       */

  /* Return the advance width table for a given pixel size if it is found  */

  /* in the font's `hdmx' table (if any).                                  */

  /*                                                                       */

  FT_LOCAL_DEF( FT_Byte* )

  tt_face_get_device_metrics( TT_Face  face,

                              FT_UInt  ppem,

                              FT_UInt  gindex )

  {

    FT_UInt   nn;

    FT_Byte*  result      = NULL;

    FT_ULong  record_size = face->hdmx_record_size;

    FT_Byte*  record      = face->hdmx_table + 8;





    for ( nn = 0; nn < face->hdmx_record_count; nn++ )

      if ( face->hdmx_record_sizes[nn] == ppem )

      {

        gindex += 2;

        if ( gindex < record_size )

          result = record + nn * record_size + gindex;

        break;

      }



    return result;

  }





/* END */

