shithub: freetype+ttf2subf

ref: ef3c12650edd2dfa462f661596f3f62843846c1c
dir: /src/otlayout/otlcommn.c/

View raw version
#include "otlayout.h"

 /*************************************************************************/
 /*************************************************************************/
 /*****                                                               *****/
 /*****                       COVERAGE TABLE                          *****/
 /*****                                                               *****/
 /*************************************************************************/
 /*************************************************************************/

  OTL_LOCALDEF( void )
  otl_coverage_validate( OTL_Bytes      table,
                         OTL_Validator  valid )
  {
    OTL_Bytes  p;
    OTL_UInt   format;

    if ( table + 4 > valid->limit )
      OTL_INVALID_TOO_SHORT;

    format = OTL_NEXT_UShort(p);
    switch (format)
    {
      case 1:
        {
          OTL_UInt  count = OTL_NEXT_UShort(p);

          if ( p + count*2 >= valid->limit )
            OTL_INVALID_TOO_SHORT;

          /* XXX: check glyph indices */
        }
        break;

      case 2:
        {
          OTL_UInt  n, num_ranges = OTL_NEXT_UShort(p);
          OTL_UInt  start, end, start_cover, total = 0, last = 0;

          if ( p + num_ranges*6 >= valid->limit )
            OTL_INVALID_TOO_SHORT;

          for ( n = 0; n < num_ranges; n++ )
          {
            start       = OTL_NEXT_UShort(p);
            end         = OTL_NEXT_UShort(p);
            start_cover = OTL_NEXT_UShort(p);

            if ( start > end || start_cover != total )
              OTL_INVALID_DATA;

            if ( n > 0 && start <= last )
              OTL_INVALID_DATA;

            total += (end - start + 1);
            last   = end;
          }
        }
        break;

      default:
        OTL_INVALID_FORMAT;
    }
  }


  OTL_LOCALDEF( OTL_UInt )
  otl_coverage_get_count( OTL_Bytes   table )
  {
    OTL_Bytes  p      = table;
    OTL_UInt   format = OTL_NEXT_UShort(p);
    OTL_UInt   count  = OTL_NEXT_UShort(p);
    OTL_UInt   result = 0;

    switch ( format )
    {
      case 1:
        return count;

      case 2:
        {
          OTL_UInt  start, end;

          for ( ; count > 0; count-- )
          {
            start = OTL_NEXT_UShort(p);
            end   = OTL_NEXT_UShort(p);
            p    += 2;  /* skip start_index */

            result += ( end - start + 1 );
          }
        }
        break;

      default:
        ;
    }

    return  result;
  }


  OTL_LOCALDEF( OTL_Int )
  otl_coverage_get_index( OTL_Bytes   table,
                          OTL_UInt    glyph_index )
  {
    OTL_Bytes  p      = table;
    OTL_UInt   format = OTL_NEXT_UShort(p);
    OTL_UInt   count  = OTL_NEXT_UShort(p);

    switch ( format )
    {
      case 1:
        {
          OTL_UInt   min = 0, max = count, mid, gindex;

          table += 4;
          while ( min < max )
          {
            mid    = ( min + max ) >> 1;
            p      = table + 2*mid;
            gindex = OTL_PEEK_UShort(p);

            if ( glyph_index == gindex )
              return (OTL_Int)mid;

            if ( glyph_index < gindex )
              max = mid;
            else
              min = mid + 1;
          }
        }
        break;

      case 2:
        {
          OTL_UInt   min = 0, max = count, mid;
          OTL_UInt   start, end, delta, start_cover;

          table += 4;
          while ( min < max )
          {
            mid    = ( min + max ) >> 1;
            p      = table + 6*mid;
            start  = OTL_NEXT_UShort(p);
            end    = OTL_NEXT_UShort(p);

            if ( glyph_index < start )
              max = mid;
            else if ( glyph_index > end )
              min = mid + 1;
            else
              return (OTL_Int)( glyph_index + OTL_NEXT_UShort(p) - start );
          }
        }
        break;

      default:
        ;
    }
    return  -1;
  }

 /*************************************************************************/
 /*************************************************************************/
 /*****                                                               *****/
 /*****                  CLASS DEFINITION TABLE                       *****/
 /*****                                                               *****/
 /*************************************************************************/
 /*************************************************************************/

  OTL_LOCALDEF( void )
  otl_class_definition_validate( OTL_Bytes      table,
                                 OTL_Validator  valid )
  {
    OTL_Bytes  p = table;
    OTL_UInt   format;

    if ( p + 4 > valid->limit )
      OTL_INVALID_TOO_SHORT;

    format = OTL_NEXT_UShort(p);
    switch ( format )
    {
      case 1:
        {
          OTL_UInt  count, start = OTL_NEXT_UShort(p);

          if ( p + 2 > valid->limit )
            OTL_INVALID_TOO_SHORT;

          count = OTL_NEXT_UShort(p);

          if ( p + count*2 > valid->limit )
            OTL_INVALID_TOO_SHORT;

          /* XXX: check glyph indices */
        }
        break;

      case 2:
        {
          OTL_UInt  n, num_ranges = OTL_NEXT_UShort(p);
          OTL_UInt  start, end, value, last = 0;

          if ( p + num_ranges*6 > valid->limit )
            OTL_INVALID_TOO_SHORT;

          for ( n = 0; n < num_ranges; n++ )
          {
            start = OTL_NEXT_UShort(p);
            end   = OTL_NEXT_UShort(p);
            value = OTL_NEXT_UShort(p);  /* ignored */

            if ( start > end || ( n > 0 && start <= last ) )
              OTL_INVALID_DATA;

            last = end;
          }
        }
        break;

      default:
        OTL_INVALID_FORMAT;
    }
  }


  OTL_LOCALDEF( OTL_UInt )
  otl_class_definition_get_value( OTL_Bytes  table,
                                  OTL_UInt   glyph_index )
  {
    OTL_Bytes  p      = table;
    OTL_UInt   format = OTL_NEXT_UShort(p);

    switch ( format )
    {
      case 1:
        {
          OTL_UInt  start = OTL_NEXT_UShort(p);
          OTL_UInt  count = OTL_NEXT_UShort(p);
          OTL_UInt  index = (OTL_UInt)( glyph_index - start );

          if ( index < count )
          {
            p += 2*index;
            return OTL_PEEK_UShort(p);
          }
        }
        break;

      case 2:
        {
          OTL_UInt  count = OTL_NEXT_UShort(p);
          OTL_UInt  min = 0, max = count, mid, gindex;

          table += 4;
          while ( min < max )
          {
            mid   = ( min + max ) >> 1;
            p     = table + 6*mid;
            start = OTL_NEXT_UShort(p);
            end   = OTL_NEXT_UShort(p);

            if ( glyph_index < start )
              max = mid;
            else if ( glyph_index > end )
              min = mid+1;
            else
              return OTL_PEEK_UShort(p);
          }
        }
        break;

      default:
        ;
    }
    return 0;
  }

 /*************************************************************************/
 /*************************************************************************/
 /*****                                                               *****/
 /*****                      DEVICE TABLE                             *****/
 /*****                                                               *****/
 /*************************************************************************/
 /*************************************************************************/

  OTL_LOCALDEF( void )
  otl_device_table_validate( OTL_Bytes      table,
                             OTL_Validator  valid )
  {
    OTL_Bytes  p = table;
    OTL_UInt   start, end, count, format, count;

    if ( p + 8 > valid->limit )
      OTL_INVALID_TOO_SHORT;

    start  = OTL_NEXT_UShort(p);
    end    = OTL_NEXT_UShort(p);
    format = OTL_NEXT_UShort(p);

    if ( format < 1 || format > 3 || end < start )
      OTL_INVALID_DATA;

    count    = (OTL_UInt)( end - start + 1 );

    if ( p + ((1 << format)*count)/8> valid->limit )
      OTL_INVALID_TOO_SHORT;
  }


  OTL_LOCALDEF( OTL_UInt )
  otl_device_table_get_start( OTL_Bytes  table )
  {
    OTL_Bytes  p = table;

    return OTL_PEEK_UShort(p);
  }


  OTL_LOCALDEF( OTL_UInt )
  otl_device_table_get_end( OTL_Bytes  table )
  {
    OTL_Bytes  p = table + 2;

    return OTL_PEEK_UShort(p);
  }


  OTL_LOCALDEF( OTL_Int )
  otl_device_table_get_delta( OTL_Bytes   table,
                              OTL_UInt    size )
  {
    OTL_Bytes   p = table;
    OTL_Int     result = 0;
    OTL_UInt    start, end, format, index, value;

    start  = OTL_NEXT_UShort(p);
    end    = OTL_NEXT_UShort(p);
    format = OTL_NEXT_UShort(p);

    if ( size >= start && size <= end )
    {
      /* we could do that with clever bit operations, but a switch is simply */
      /* much simpler to understand and maintain !!                          */
      /*                                                                     */
      switch ( format )
      {
        case 1:
          {
            index   = (OTL_UInt)(( size - start )*2);
            p      += (index/16);
            value   = OTL_PEEK_UShort(p);
            shift   = index & 15;
            result  = (OTL_Short)(value << shift) >> (14-shift);
          }
          break;

        case 2:
          {
            index   = (OTL_UInt)(( size - start )*4);
            p      += (index/16);
            value   = OTL_PEEK_UShort(p);
            shift   = index & 15;
            result  = (OTL_Short)(value << shift) >> (12-shift);
          }
          break;

        case 3:
          {
            index   = (OTL_UInt)(( size - start )*8);
            p      += (index/16);
            value   = OTL_PEEK_UShort(p);
            shift   = index & 15;
            result  = (OTL_Short)(value << shift) >> (8-shift);
          }
          break;

        default:
          ;
      }
    }
    return  result;
  }

 /*************************************************************************/
 /*************************************************************************/
 /*****                                                               *****/
 /*****                      LOOKUP LISTS                             *****/
 /*****                                                               *****/
 /*************************************************************************/
 /*************************************************************************/

  OTL_LOCALDEF( void )
  otl_lookup_validate( OTL_Bytes      table,
                       OTL_Validator  valid )
  {
    OTL_Bytes  p = table;
    OTL_UInt   num_tables;

    if ( table + 6 > valid->limit )
      OTL_INVALID_TOO_SHORT;

    p += 4;
    num_tables = OTL_NEXT_UShort(p);

    if ( p + num_tables*2 > valid->limit )
      OTL_INVALID_TOO_SHORT;

    for ( ; num_tables > 0; num_tables-- )
    {
      offset = OTL_NEXT_UShort(p);

      if ( table + offset >= valid->limit )
        OTL_INVALID_OFFSET;
    }

    /* XXX: check sub-tables ?? */
  }


  OTL_LOCALDEF( OTL_UInt )
  otl_lookup_get_count( OTL_Bytes  table )
  {
    OTL_Bytes  p = table + 4;

    return  OTL_PEEK_UShort(p);
  }


  OTL_LOCALDEF( OTL_Bytes )
  otl_lookup_get_table( OTL_Bytes  table,
                        OTL_UInt   index )
  {
    OTL_Bytes  p, result = NULL;
    OTL_UInt   count;

    p     = table + 4;
    count = OTL_NEXT_UShort(p);
    if ( index < count )
    {
      p     += index*2;
      result = table + OTL_PEEK_UShort(p);
    }
    return  result;
  }

 /*************************************************************************/
 /*************************************************************************/
 /*****                                                               *****/
 /*****                      LOOKUP LISTS                             *****/
 /*****                                                               *****/
 /*************************************************************************/
 /*************************************************************************/

  OTL_LOCALDEF( void )
  otl_lookup_list_validate( OTL_Bytes      table,
                            OTL_Validator  valid )
  {
    OTL_Bytes  p = table, q;
    OTL_UInt   num_lookups, offset;

    if ( p + 2 > valid->limit )
      OTL_INVALID_TOO_SHORT;

    num_lookups = OTL_NEXT_UShort(p);

    if ( p + num_lookups*2 > valid->limit )
      OTL_INVALID_TOO_SHORT;

    for ( ; num_lookups > 0; num_lookups-- )
    {

      offset = OTL_NEXT_UShort(p);

      otl_lookup_validate( table + offset, valid );
    }
  }


  OTL_LOCALDEF( OTL_UInt )
  otl_lookup_list_get_count( OTL_Bytes  table )
  {
    OTL_Bytes  p = table;

    return OTL_PEEK_UShort(p);
  }


  OTL_LOCALDEF( OTL_Bytes )
  otl_lookup_list_get_lookup( OTL_Bytes  table,
                              OTL_UInt   index )
  {
    OTL_Bytes  p, result = 0;
    OTL_UInt   count;

    p     = table;
    count = OTL_NEXT_UShort(p);
    if ( index < count )
    {
      p     += index*2;
      result = table + OTL_PEEK_UShort(p);
    }
    return  result;
  }


  OTL_LOCALDEF( OTL_Bytes )
  otl_lookup_list_get_table( OTL_Bytes  table,
                             OTL_UInt   lookup_index,
                             OTL_UInt   table_index )
  {
    OTL_Bytes  result = NULL;

    result = otl_lookup_list_get_lookup( table, lookup_index );
    if ( result )
      result = otl_lookup_get_table( result, table_index );

    return result;
  }


  OTL_LOCALDEF( void )
  otl_lookup_list_foreach( OTL_Bytes        table,
                           OTL_ForeachFunc  func,
                           OTL_Pointer      func_data )
  {
    OTL_Bytes  p     = table;
    OTL_UInt   count = OTL_NEXT_UShort(p);

    for ( ; count > 0; count-- )
      func( table + OTL_NEXT_USHORT(p), func_data );
  }


 /*************************************************************************/
 /*************************************************************************/
 /*****                                                               *****/
 /*****                        FEATURES                               *****/
 /*****                                                               *****/
 /*************************************************************************/
 /*************************************************************************/

  OTL_LOCALDEF( void )
  otl_feature_validate( OTL_Bytes      table,
                        OTL_Validator  valid )
  {
    OTL_Bytes   p = table;
    OTL_UInt    feat_params, num_lookups;

    if ( p + 4 > valid->limit )
      OTL_INVALID_TOO_SHORT;

    feat_params = OTL_NEXT_UShort(p);  /* ignored */
    num_lookups = OTL_NEXT_UShort(p);

    if ( p + num_lookups*2 > valid->limit )
      OTL_INVALID_TOO_SHORT;

    /* XXX: check lookup indices */
  }


  OTL_LOCALDEF( OTL_UInt )
  otl_feature_get_count( OTL_Bytes   table )
  {
    OTL_Bytes  p = table + 4;

    return OTL_PEEK_UShort(p);
  }


  OTL_LOCALDEF( OTL_UInt )
  otl_feature_get_lookups( OTL_Bytes   table,
                           OTL_UInt    start,
                           OTL_UInt    count,
                           OTL_UInt   *lookups )
  {
    OTL_Bytes  p;
    OTL_UInt   num_features, result = 0;

    p            = table + 4;
    num_features = OTL_NEXT_UShort(p);

    p += start*2;

    for ( ; count > 0 && start < num_features; count--, start++ )
    {
      lookups[0] = OTL_NEXT_UShort(p);
      lookups++;
      result++;
    }

    return result;
  }



 /*************************************************************************/
 /*************************************************************************/
 /*****                                                               *****/
 /*****                        FEATURE LIST                           *****/
 /*****                                                               *****/
 /*************************************************************************/
 /*************************************************************************/

  OTL_LOCALDEF( void )
  otl_feature_list_validate( OTL_Bytes      table,
                             OTL_Validator  valid )
  {
    OTL_Bytes  p = table;
    OTL_UInt   num_features, offset;

    if ( table + 2 > valid->limit )
      OTL_INVALID_TOO_SHORT;

    num_features = OTL_NEXT_UShort(p);

    if ( p + num_features*2 > valid->limit )
      OTL_INVALID_TOO_SHORT;

    for ( ; num_features > 0; num_features-- )
    {
      p     += 4;   /* skip tag */
      offset = OTL_NEXT_UShort(p);

      otl_feature_table_validate( table + offset, valid );
    }
  }


  OTL_LOCALDEF( OTL_UInt )
  otl_feature_list_get_count( OTL_Bytes  table )
  {
    OTL_Bytes  p = table;

    return OTL_PEEK_UShort(p);
  }


  OTL_LOCALDEF( OTL_Bytes )
  otl_feature_list_get_feature( OTL_Bytes  table,
                                OTL_UInt   index )
  {
    OTL_Bytes  p, result = NULL;
    OTL_UInt   count;

    p     = table;
    count = OTL_NEXT_UShort(p);

    if ( index < count )
    {
      p     += index*2;
      result = table + OTL_PEEK_UShort(p);
    }
    return  result;
  }


  OTL_LOCALDEF( void )
  otl_feature_list_foreach( OTL_Bytes        table,
                            OTL_ForeachFunc  func,
                            OTL_Pointer      func_data )
  {
    OTL_Bytes   p;
    OTL_UInt    count;

    p = table;
    count = OTL_NEXT_UShort(p);

    for ( ; count > 0; count-- )
      func( table + OTL_NEXT_UShort(p), func_data );
  }

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


  OTL_LOCALDEF( void )
  otl_lang_validate( OTL_Bytes      table,
                     OTL_Validator  valid )
  {
    OTL_Bytes  p = table;
    OTL_UInt   lookup_order;
    OTL_UInt   req_feature;
    OTL_UInt   num_features;

    if ( table + 6 >= valid->limit )
      OTL_INVALID_TOO_SHORT;

    lookup_order = OTL_NEXT_UShort(p);
    req_feature  = OTL_NEXT_UShort(p);
    num_features = OTL_NEXT_UShort(p);

    /* XXX: check req_feature if not 0xFFFFU */

    if ( p + 2*num_features >= valid->limit )
      OTL_INVALID_TOO_SHORT;

    /* XXX: check features indices !! */
  }


  OTL_LOCALDEF( OTL_UInt )
  otl_lang_get_count( OTL_Bytes  table )
  {
    OTL_Bytes   p  = table + 4;

    return OTL_PEEK_UShort(p);
  }


  OTL_LOCALDEF( OTL_UInt )
  otl_lang_get_features( OTL_Bytes  table,
                         OTL_UInt   start,
                         OTL_UInt   count,
                         OTL_UInt  *features )
  {
    OTL_Bytes   p            = table + 4;
    OTL_UInt    num_features = OTL_NEXT_UShort(p);
    OTL_UInt    result       = 0;

    p += start*2;

    for ( ; count > 0 && start < num_features; start++, count-- )
    {
      features[0] = OTL_NEXT_UShort(p);
      features++;
      result++;
    }
    return  result;
  }


  OTL_LOCALDEF( OTL_UInt )
  otl_lang_get_req_feature( OTL_Bytes   table )
  {
    OTL_Bytes  p = table + 2;

    return OTL_PEEK_UShort(p);
  }



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


  OTL_LOCALDEF( void )
  otl_script_table_validate( OTL_Bytes      table,
                             OTL_Validator  valid )
  {
    OTL_UInt   default_lang;
    OTL_Bytes  p = table;

    if ( table + 4 > valid->limit )
      OTL_INVALID_TOO_SHORT;

    default_lang = OTL_NEXT_UShort(p);
    num_langs    = OTL_NEXT_UShort(p);

    if ( default_lang != 0 )
    {
      if ( table + default_lang >= valid->limit )
        OTL_INVALID_OFFSET;
    }

    if ( p + num_langs*6 >= valid->limit )
      OTL_INVALID_OFFSET;

    for ( ; num_langs > 0; num_langs-- )
    {
      OTL_UInt  offset;

      p     += 4;  /* skip tag */
      offset = OTL_NEXT_UShort(p);

      otl_lang_validate( table + offset, valid );
    }
  }


  OTL_LOCALDEF( void )
  otl_script_list_validate( OTL_Bytes      list,
                            OTL_Validator  valid )
  {
    OTL_UInt  num_scripts;
    OTL_Bytes p = list;

    if ( list + 2 > valid->limit )
      OTL_INVALID_TOO_SHORT;

    num_scripts = OTL_NEXT_UShort(p);

    if ( p + num_scripts*6 > valid->limit )
      OTL_INVALID_TOO_SHORT;

    for ( ; num_scripts > 0; num_scripts-- )
    {
      OTL_UInt  offset;

      p     += 4; /* skip tag */
      offset = OTL_NEXT_UShort(p);

      otl_script_table_validate( list + offset, valid );
    }
  }