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

/*                                                                         */

/*  ttmtx.c                                                                */

/*                                                                         */

/*    Load the metrics tables common to TTF and OTF fonts (body).          */

/*                                                                         */

/*  Copyright 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_STREAM_H

#include FT_TRUETYPE_TAGS_H

#include "ttmtx.h"



#include "sferrors.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_ttmtx





  /*

   *  Unfortunately, we can't enable our memory optimizations if

   *  FT_CONFIG_OPTION_OLD_INTERNALS is defined.  This is because at least

   *  one rogue client (libXfont in the X.Org XServer) is directly accessing

   *  the metrics.

   */



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

  /*                                                                       */

  /* <Function>                                                            */

  /*    tt_face_load_hmtx                                                  */

  /*                                                                       */

  /* <Description>                                                         */

  /*    Load the `hmtx' or `vmtx' table into a face object.                */

  /*                                                                       */

  /* <Input>                                                               */

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

  /*                                                                       */

  /*    stream   :: The input stream.                                      */

  /*                                                                       */

  /*    vertical :: A boolean flag.  If set, load `vmtx'.                  */

  /*                                                                       */

  /* <Return>                                                              */

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

  /*                                                                       */

#ifndef FT_CONFIG_OPTION_OLD_INTERNALS



  FT_LOCAL_DEF( FT_Error )

  tt_face_load_hmtx( TT_Face    face,

                     FT_Stream  stream,

                     FT_Bool    vertical )

  {

    FT_Error   error;

    FT_ULong   tag, table_size;

    FT_ULong*  ptable_offset;

    FT_ULong*  ptable_size;





    if ( vertical )

    {

      tag           = TTAG_vmtx;

      ptable_offset = &face->vert_metrics_offset;

      ptable_size   = &face->vert_metrics_size;

    }

    else

    {

      tag           = TTAG_hmtx;

      ptable_offset = &face->horz_metrics_offset;

      ptable_size   = &face->horz_metrics_size;

    }



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

    if ( error )

      goto Fail;



    *ptable_size   = table_size;

    *ptable_offset = FT_STREAM_POS();



  Fail:

    return error;

  }



#else /* !FT_CONFIG_OPTION_OLD_INTERNALS */



  FT_LOCAL_DEF( FT_Error )

  tt_face_load_hmtx( TT_Face    face,

                     FT_Stream  stream,

                     FT_Bool    vertical )

  {

    FT_Error   error;

    FT_Memory  memory = stream->memory;



    FT_ULong   table_len;

    FT_Long    num_shorts, num_longs, num_shorts_checked;



    TT_LongMetrics*    longs;

    TT_ShortMetrics**  shorts;

    FT_Byte*           p;





    if ( vertical )

    {

      void*   lm = &face->vertical.long_metrics;

      void**  sm = &face->vertical.short_metrics;





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

      if ( error )

        goto Fail;



      num_longs = face->vertical.number_Of_VMetrics;

      if ( (FT_ULong)num_longs > table_len / 4 )

        num_longs = (FT_Long)( table_len / 4 );



      face->vertical.number_Of_VMetrics = 0;



      longs  = (TT_LongMetrics*)lm;

      shorts = (TT_ShortMetrics**)sm;

    }

    else

    {

      void*   lm = &face->horizontal.long_metrics;

      void**  sm = &face->horizontal.short_metrics;





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

      if ( error )

        goto Fail;



      num_longs = face->horizontal.number_Of_HMetrics;

      if ( (FT_ULong)num_longs > table_len / 4 )

        num_longs = (FT_Long)( table_len / 4 );



      face->horizontal.number_Of_HMetrics = 0;



      longs  = (TT_LongMetrics*)lm;

      shorts = (TT_ShortMetrics**)sm;

    }



    /* never trust derived values */



    num_shorts         = face->max_profile.numGlyphs - num_longs;

    num_shorts_checked = ( table_len - num_longs * 4L ) / 2;



    if ( num_shorts < 0 )

    {

      FT_TRACE0(( "tt_face_load_hmtx:"

                  " %cmtx has more metrics than glyphs.\n",

                  vertical ? "v" : "h" ));



      /* Adobe simply ignores this problem.  So we shall do the same. */

#if 0

      error = vertical ? SFNT_Err_Invalid_Vert_Metrics

                       : SFNT_Err_Invalid_Horiz_Metrics;

      goto Exit;

#else

      num_shorts = 0;

#endif

    }



    if ( FT_QNEW_ARRAY( *longs,  num_longs  ) ||

         FT_QNEW_ARRAY( *shorts, num_shorts ) )

      goto Fail;



    if ( FT_FRAME_ENTER( table_len ) )

      goto Fail;



    p = stream->cursor;



    {

      TT_LongMetrics  cur   = *longs;

      TT_LongMetrics  limit = cur + num_longs;





      for ( ; cur < limit; cur++ )

      {

        cur->advance = FT_NEXT_USHORT( p );

        cur->bearing = FT_NEXT_SHORT( p );

      }

    }



    /* do we have an inconsistent number of metric values? */

    {

      TT_ShortMetrics*  cur   = *shorts;

      TT_ShortMetrics*  limit = cur +

                                FT_MIN( num_shorts, num_shorts_checked );





      for ( ; cur < limit; cur++ )

        *cur = FT_NEXT_SHORT( p );



      /* We fill up the missing left side bearings with the     */

      /* last valid value.  Since this will occur for buggy CJK */

      /* fonts usually only, nothing serious will happen.       */

      if ( num_shorts > num_shorts_checked && num_shorts_checked > 0 )

      {

        FT_Short  val = (*shorts)[num_shorts_checked - 1];





        limit = *shorts + num_shorts;

        for ( ; cur < limit; cur++ )

          *cur = val;

      }

    }



    FT_FRAME_EXIT();



    if ( vertical )

      face->vertical.number_Of_VMetrics = (FT_UShort)num_longs;

    else

      face->horizontal.number_Of_HMetrics = (FT_UShort)num_longs;



  Fail:

    return error;

  }



#endif /* !FT_CONFIG_OPTION_OLD_INTERNALS */





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

  /*                                                                       */

  /* <Function>                                                            */

  /*    tt_face_load_hhea                                                  */

  /*                                                                       */

  /* <Description>                                                         */

  /*    Load the `hhea' or 'vhea' table into a face object.                */

  /*                                                                       */

  /* <Input>                                                               */

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

  /*                                                                       */

  /*    stream   :: The input stream.                                      */

  /*                                                                       */

  /*    vertical :: A boolean flag.  If set, load `vhea'.                  */

  /*                                                                       */

  /* <Return>                                                              */

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

  /*                                                                       */

  FT_LOCAL_DEF( FT_Error )

  tt_face_load_hhea( TT_Face    face,

                     FT_Stream  stream,

                     FT_Bool    vertical )

  {

    FT_Error        error;

    TT_HoriHeader*  header;



    const FT_Frame_Field  metrics_header_fields[] =

    {

#undef  FT_STRUCTURE

#define FT_STRUCTURE  TT_HoriHeader



      FT_FRAME_START( 36 ),

        FT_FRAME_ULONG ( Version ),

        FT_FRAME_SHORT ( Ascender ),

        FT_FRAME_SHORT ( Descender ),

        FT_FRAME_SHORT ( Line_Gap ),

        FT_FRAME_USHORT( advance_Width_Max ),

        FT_FRAME_SHORT ( min_Left_Side_Bearing ),

        FT_FRAME_SHORT ( min_Right_Side_Bearing ),

        FT_FRAME_SHORT ( xMax_Extent ),

        FT_FRAME_SHORT ( caret_Slope_Rise ),

        FT_FRAME_SHORT ( caret_Slope_Run ),

        FT_FRAME_SHORT ( caret_Offset ),

        FT_FRAME_SHORT ( Reserved[0] ),

        FT_FRAME_SHORT ( Reserved[1] ),

        FT_FRAME_SHORT ( Reserved[2] ),

        FT_FRAME_SHORT ( Reserved[3] ),

        FT_FRAME_SHORT ( metric_Data_Format ),

        FT_FRAME_USHORT( number_Of_HMetrics ),

      FT_FRAME_END

    };





    if ( vertical )

    {

      void  *v = &face->vertical;





      error = face->goto_table( face, TTAG_vhea, stream, 0 );

      if ( error )

        goto Fail;



      header = (TT_HoriHeader*)v;

    }

    else

    {

      error = face->goto_table( face, TTAG_hhea, stream, 0 );

      if ( error )

        goto Fail;



      header = &face->horizontal;

    }



    if ( FT_STREAM_READ_FIELDS( metrics_header_fields, header ) )

      goto Fail;



    FT_TRACE3(( "Ascender:          %5d\n", header->Ascender ));

    FT_TRACE3(( "Descender:         %5d\n", header->Descender ));

    FT_TRACE3(( "number_Of_Metrics: %5u\n", header->number_Of_HMetrics ));



    header->long_metrics  = NULL;

    header->short_metrics = NULL;



  Fail:

    return error;

  }





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

  /*                                                                       */

  /* <Function>                                                            */

  /*    tt_face_get_metrics                                                */

  /*                                                                       */

  /* <Description>                                                         */

  /*    Returns the horizontal or vertical metrics in font units for a     */

  /*    given glyph.  The metrics are the left side bearing (resp. top     */

  /*    side bearing) and advance width (resp. advance height).            */

  /*                                                                       */

  /* <Input>                                                               */

  /*    header  :: A pointer to either the horizontal or vertical metrics  */

  /*               structure.                                              */

  /*                                                                       */

  /*    idx     :: The glyph index.                                        */

  /*                                                                       */

  /* <Output>                                                              */

  /*    bearing :: The bearing, either left side or top side.              */

  /*                                                                       */

  /*    advance :: The advance width resp. advance height.                 */

  /*                                                                       */

#ifndef FT_CONFIG_OPTION_OLD_INTERNALS



  FT_LOCAL_DEF( FT_Error )

  tt_face_get_metrics( TT_Face     face,

                       FT_Bool     vertical,

                       FT_UInt     gindex,

                       FT_Short   *abearing,

                       FT_UShort  *aadvance )

  {

    FT_Error        error;

    FT_Stream       stream = face->root.stream;

    TT_HoriHeader*  header;

    FT_ULong        table_pos, table_size, table_end;

    FT_UShort       k;





    if ( vertical )

    {

      void*  v = &face->vertical;





      header     = (TT_HoriHeader*)v;

      table_pos  = face->vert_metrics_offset;

      table_size = face->vert_metrics_size;

    }

    else

    {

      header     = &face->horizontal;

      table_pos  = face->horz_metrics_offset;

      table_size = face->horz_metrics_size;

    }



    table_end = table_pos + table_size;



    k = header->number_Of_HMetrics;



    if ( k > 0 )

    {

      if ( gindex < (FT_UInt)k )

      {

        table_pos += 4 * gindex;

        if ( table_pos + 4 > table_end )

          goto NoData;



        if ( FT_STREAM_SEEK( table_pos ) ||

             FT_READ_USHORT( *aadvance ) ||

             FT_READ_SHORT( *abearing )  )

          goto NoData;

      }

      else

      {

        table_pos += 4 * ( k - 1 );

        if ( table_pos + 4 > table_end )

          goto NoData;



        if ( FT_STREAM_SEEK( table_pos ) ||

             FT_READ_USHORT( *aadvance ) )

          goto NoData;



        table_pos += 4 + 2 * ( gindex - k );

        if ( table_pos + 2 > table_end )

          *abearing = 0;

        else

        {

          if ( !FT_STREAM_SEEK( table_pos ) )

            (void)FT_READ_SHORT( *abearing );

        }

      }

    }

    else

    {

    NoData:

      *abearing = 0;

      *aadvance = 0;

    }



    return SFNT_Err_Ok;

  }



#else /* !FT_CONFIG_OPTION_OLD_INTERNALS */



  FT_LOCAL_DEF( FT_Error )

  tt_face_get_metrics( TT_Face     face,

                       FT_Bool     vertical,

                       FT_UInt     gindex,

                       FT_Short*   abearing,

                       FT_UShort*  aadvance )

  {

    void*           v = &face->vertical;

    void*           h = &face->horizontal;

    TT_HoriHeader*  header = vertical ? (TT_HoriHeader*)v

                                      : (TT_HoriHeader*)h;

    TT_LongMetrics  longs_m;

    FT_UShort       k = header->number_Of_HMetrics;





    if ( k == 0                                         ||

         !header->long_metrics                          ||

         gindex >= (FT_UInt)face->max_profile.numGlyphs )

    {

      *abearing = *aadvance = 0;

      return SFNT_Err_Ok;

    }



    if ( gindex < (FT_UInt)k )

    {

      longs_m   = (TT_LongMetrics)header->long_metrics + gindex;

      *abearing = longs_m->bearing;

      *aadvance = longs_m->advance;

    }

    else

    {

      *abearing = ((TT_ShortMetrics*)header->short_metrics)[gindex - k];

      *aadvance = ((TT_LongMetrics)header->long_metrics)[k - 1].advance;

    }



    return SFNT_Err_Ok;

  }



#endif /* !FT_CONFIG_OPTION_OLD_INTERNALS */





/* END */

