code:
/***************************************************************************/
/*                                                                         */
/*  gxvkern.c                                                              */
/*                                                                         */
/*    TrueTypeGX/AAT kern table validation (body).                         */
/*                                                                         */
/*  Copyright 2004, 2005, 2006, 2007                                       */
/*  by suzuki toshiya, Masatake YAMATO, Red Hat K.K.,                      */
/*  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.                                        */
/*                                                                         */
/***************************************************************************/

/***************************************************************************/
/*                                                                         */
/* gxvalid is derived from both gxlayout module and otvalid module.        */
/* Development of gxlayout is supported by the Information-technology      */
/* Promotion Agency(IPA), Japan.                                           */
/*                                                                         */
/***************************************************************************/

#include "gxvalid.h"
#include "gxvcommn.h"

#include FT_SFNT_NAMES_H
#include FT_SERVICE_GX_VALIDATE_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_gxvkern

  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                      Data and Types                           *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/

  typedef enum  GXV_kern_Version_
  {
    KERN_VERSION_CLASSIC = 0x0000,
    KERN_VERSION_NEW     = 0x0001

  } GXV_kern_Version;

  typedef enum GXV_kern_Dialect_
  {
    KERN_DIALECT_UNKNOWN = 0,
    KERN_DIALECT_MS      = FT_VALIDATE_MS,
    KERN_DIALECT_APPLE   = FT_VALIDATE_APPLE,
    KERN_DIALECT_ANY     = FT_VALIDATE_CKERN

  } GXV_kern_Dialect;

  typedef struct  GXV_kern_DataRec_
  {
    GXV_kern_Version  version;
    void             *subtable_data;
    GXV_kern_Dialect  dialect_request;

  } GXV_kern_DataRec, *GXV_kern_Data;

#define GXV_KERN_DATA( field )  GXV_TABLE_DATA( kern, field )

#define KERN_IS_CLASSIC( valid )                               \
          ( KERN_VERSION_CLASSIC == GXV_KERN_DATA( version ) )
#define KERN_IS_NEW( valid )                                   \
          ( KERN_VERSION_NEW     == GXV_KERN_DATA( version ) )

#define KERN_DIALECT( valid )              \
          GXV_KERN_DATA( dialect_request )
#define KERN_ALLOWS_MS( valid )                       \
          ( KERN_DIALECT( valid ) & KERN_DIALECT_MS )
#define KERN_ALLOWS_APPLE( valid )                       \
          ( KERN_DIALECT( valid ) & KERN_DIALECT_APPLE )

#define GXV_KERN_HEADER_SIZE           ( KERN_IS_NEW( valid ) ? 8 : 4 )
#define GXV_KERN_SUBTABLE_HEADER_SIZE  ( KERN_IS_NEW( valid ) ? 8 : 6 )

  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                      SUBTABLE VALIDATORS                      *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/

  /* ============================= format 0 ============================== */

  static void
  gxv_kern_subtable_fmt0_pairs_validate( FT_Bytes       table,
                                         FT_Bytes       limit,
                                         FT_UShort      nPairs,
                                         GXV_Validator  valid )
  {
    FT_Bytes   p = table;
    FT_UShort  i;

    FT_UShort  last_gid_left  = 0;
    FT_UShort  last_gid_right = 0;

    FT_UNUSED( limit );

    GXV_NAME_ENTER( "kern format 0 pairs" );

    for ( i = 0; i < nPairs; i++ )
    {
      FT_UShort  gid_left;
      FT_UShort  gid_right;
      FT_Short   kernValue;

      /* left */
      gid_left  = FT_NEXT_USHORT( p );
      gxv_glyphid_validate( gid_left, valid );

      /* right */
      gid_right = FT_NEXT_USHORT( p );
      gxv_glyphid_validate( gid_right, valid );

      /* Pairs of left and right GIDs must be unique and sorted. */
      GXV_TRACE(( "left gid = %u, right gid = %u\n", gid_left, gid_right ));
      if ( gid_left == last_gid_left )
      {
        if ( last_gid_right < gid_right )
          last_gid_right = gid_right;
        else
          FT_INVALID_DATA;
      }
      else if ( last_gid_left < gid_left )
      {
        last_gid_left  = gid_left;
        last_gid_right = gid_right;
      }
      else
        FT_INVALID_DATA;

      /* skip the kern value */
      kernValue = FT_NEXT_SHORT( p );
    }

    GXV_EXIT;
  }

  static void
  gxv_kern_subtable_fmt0_validate( FT_Bytes       table,
                                   FT_Bytes       limit,
                                   GXV_Validator  valid )
  {
    FT_Bytes   p = table + GXV_KERN_SUBTABLE_HEADER_SIZE;

    FT_UShort  nPairs;
    FT_UShort  unitSize;

    GXV_NAME_ENTER( "kern subtable format 0" );

    unitSize = 2 + 2 + 2;
    nPairs   = 0;

    /* nPairs, searchRange, entrySelector, rangeShift */
    GXV_LIMIT_CHECK( 2 + 2 + 2 + 2 );
    gxv_BinSrchHeader_validate( p, limit, &unitSize, &nPairs, valid );
    p += 2 + 2 + 2 + 2;

    gxv_kern_subtable_fmt0_pairs_validate( p, limit, nPairs, valid );

    GXV_EXIT;
  }

  /* ============================= format 1 ============================== */

  typedef struct  GXV_kern_fmt1_StateOptRec_
  {
    FT_UShort  valueTable;
    FT_UShort  valueTable_length;

  } GXV_kern_fmt1_StateOptRec, *GXV_kern_fmt1_StateOptRecData;

  static void
  gxv_kern_subtable_fmt1_valueTable_load( FT_Bytes       table,
                                          FT_Bytes       limit,
                                          GXV_Validator  valid )
  {
    FT_Bytes                       p = table;
    GXV_kern_fmt1_StateOptRecData  optdata =
      (GXV_kern_fmt1_StateOptRecData)valid->statetable.optdata;

    GXV_LIMIT_CHECK( 2 );
    optdata->valueTable = FT_NEXT_USHORT( p );
  }

  /*
   * passed tables_size covers whole StateTable, including kern fmt1 header
   */
  static void
  gxv_kern_subtable_fmt1_subtable_setup( FT_UShort      table_size,
                                         FT_UShort      classTable,
                                         FT_UShort      stateArray,
                                         FT_UShort      entryTable,
                                         FT_UShort*     classTable_length_p,
                                         FT_UShort*     stateArray_length_p,
                                         FT_UShort*     entryTable_length_p,
                                         GXV_Validator  valid )
  {
    FT_UShort  o[4];
    FT_UShort  *l[4];
    FT_UShort  buff[5];

    GXV_kern_fmt1_StateOptRecData  optdata =
      (GXV_kern_fmt1_StateOptRecData)valid->statetable.optdata;

    o[0] = classTable;
    o[1] = stateArray;
    o[2] = entryTable;
    o[3] = optdata->valueTable;
    l[0] = classTable_length_p;
    l[1] = stateArray_length_p;
    l[2] = entryTable_length_p;
    l[3] = &(optdata->valueTable_length);

    gxv_set_length_by_ushort_offset( o, l, buff, 4, table_size, valid );
  }

  /*
   * passed table & limit are of whole StateTable, not including subtables
   */
  static void
  gxv_kern_subtable_fmt1_entry_validate(
    FT_Byte                         state,
    FT_UShort                       flags,
    GXV_StateTable_GlyphOffsetCPtr  glyphOffset_p,
    FT_Bytes                        table,
    FT_Bytes                        limit,
    GXV_Validator                   valid )
  {
    FT_UShort  push;
    FT_UShort  dontAdvance;
    FT_UShort  valueOffset;
    FT_UShort  kernAction;
    FT_UShort  kernValue;

    FT_UNUSED( state );
    FT_UNUSED( glyphOffset_p );

    push        = (FT_UShort)( ( flags >> 15 ) & 1      );
    dontAdvance = (FT_UShort)( ( flags >> 14 ) & 1      );
    valueOffset = (FT_UShort)(   flags         & 0x3FFF );

    {
      GXV_kern_fmt1_StateOptRecData  vt_rec =
        (GXV_kern_fmt1_StateOptRecData)valid->statetable.optdata;
      FT_Bytes  p;

      if ( valueOffset < vt_rec->valueTable )
        FT_INVALID_OFFSET;

      p     = table + valueOffset;
      limit = table + vt_rec->valueTable + vt_rec->valueTable_length;

      GXV_LIMIT_CHECK( 2 + 2 );
      kernAction = FT_NEXT_USHORT( p );
      kernValue  = FT_NEXT_USHORT( p );
    }
  }

  static void
  gxv_kern_subtable_fmt1_validate( FT_Bytes       table,
                                   FT_Bytes       limit,
                                   GXV_Validator  valid )
  {
    FT_Bytes                   p = table;
    GXV_kern_fmt1_StateOptRec  vt_rec;

    GXV_NAME_ENTER( "kern subtable format 1" );

    valid->statetable.optdata =
      &vt_rec;
    valid->statetable.optdata_load_func =
      gxv_kern_subtable_fmt1_valueTable_load;
    valid->statetable.subtable_setup_func =
      gxv_kern_subtable_fmt1_subtable_setup;
    valid->statetable.entry_glyphoffset_fmt =
      GXV_GLYPHOFFSET_NONE;
    valid->statetable.entry_validate_func =
      gxv_kern_subtable_fmt1_entry_validate;

    gxv_StateTable_validate( p, limit, valid );

    GXV_EXIT;
  }

  /* ================ Data for Class-Based Subtables 2, 3 ================ */

  typedef enum  GXV_kern_ClassSpec_
  {
    GXV_KERN_CLS_L = 0,
    GXV_KERN_CLS_R

  } GXV_kern_ClassSpec;

  /* ============================= format 2 ============================== */

  /* ---------------------- format 2 specific data ----------------------- */

  typedef struct  GXV_kern_subtable_fmt2_DataRec_
  {
    FT_UShort         rowWidth;
    FT_UShort         array;
    FT_UShort         offset_min[2];
    FT_UShort         offset_max[2];
    const FT_String*  class_tag[2];
    GXV_odtect_Range  odtect;

  } GXV_kern_subtable_fmt2_DataRec, *GXV_kern_subtable_fmt2_Data;

#define GXV_KERN_FMT2_DATA( field )                         \
        ( ( (GXV_kern_subtable_fmt2_DataRec *)              \
              ( GXV_KERN_DATA( subtable_data ) ) )->field )

  /* -------------------------- utility functions ----------------------- */

  static void
  gxv_kern_subtable_fmt2_clstbl_validate( FT_Bytes            table,
                                          FT_Bytes            limit,
                                          GXV_kern_ClassSpec  spec,
                                          GXV_Validator       valid )
  {
    const FT_String*  tag    = GXV_KERN_FMT2_DATA( class_tag[spec] );
    GXV_odtect_Range  odtect = GXV_KERN_FMT2_DATA( odtect );

    FT_Bytes   p = table;
    FT_UShort  firstGlyph;
    FT_UShort  nGlyphs;

    GXV_NAME_ENTER( "kern format 2 classTable" );

    GXV_LIMIT_CHECK( 2 + 2 );
    firstGlyph = FT_NEXT_USHORT( p );
    nGlyphs    = FT_NEXT_USHORT( p );
    GXV_TRACE(( " %s firstGlyph=%d, nGlyphs=%d\n",
                tag, firstGlyph, nGlyphs ));

    gxv_glyphid_validate( firstGlyph, valid );
    gxv_glyphid_validate( (FT_UShort)( firstGlyph + nGlyphs - 1 ), valid );

    gxv_array_getlimits_ushort( p, p + ( 2 * nGlyphs ),
                                &( GXV_KERN_FMT2_DATA( offset_min[spec] ) ),
                                &( GXV_KERN_FMT2_DATA( offset_max[spec] ) ),
                                valid );

    gxv_odtect_add_range( table, 2 * nGlyphs, tag, odtect );

    GXV_EXIT;
  }

  static void
  gxv_kern_subtable_fmt2_validate( FT_Bytes       table,
                                   FT_Bytes       limit,
                                   GXV_Validator  valid )
  {
    GXV_ODTECT( 3, odtect );
    GXV_kern_subtable_fmt2_DataRec  fmt2_rec =
      { 0, 0, { 0, 0 }, { 0, 0 }, { "leftClass", "rightClass" }, NULL };

    FT_Bytes   p = table + GXV_KERN_SUBTABLE_HEADER_SIZE;
    FT_UShort  leftOffsetTable;
    FT_UShort  rightOffsetTable;

    GXV_NAME_ENTER( "kern subtable format 2" );

    GXV_ODTECT_INIT( odtect );
    fmt2_rec.odtect = odtect;
    GXV_KERN_DATA( subtable_data ) = &fmt2_rec;

    GXV_LIMIT_CHECK( 2 + 2 + 2 + 2 );
    GXV_KERN_FMT2_DATA( rowWidth ) = FT_NEXT_USHORT( p );
    leftOffsetTable                = FT_NEXT_USHORT( p );
    rightOffsetTable               = FT_NEXT_USHORT( p );
    GXV_KERN_FMT2_DATA( array )    = FT_NEXT_USHORT( p );

    GXV_TRACE(( "rowWidth = %d\n", GXV_KERN_FMT2_DATA( rowWidth ) ));

    GXV_LIMIT_CHECK( leftOffsetTable );
    GXV_LIMIT_CHECK( rightOffsetTable );
    GXV_LIMIT_CHECK( GXV_KERN_FMT2_DATA( array ) );

    gxv_kern_subtable_fmt2_clstbl_validate( table + leftOffsetTable, limit,
                                            GXV_KERN_CLS_L, valid );

    gxv_kern_subtable_fmt2_clstbl_validate( table + rightOffsetTable, limit,
                                            GXV_KERN_CLS_R, valid );

    if ( GXV_KERN_FMT2_DATA( offset_min[GXV_KERN_CLS_L] ) +
           GXV_KERN_FMT2_DATA( offset_min[GXV_KERN_CLS_R] )
         < GXV_KERN_FMT2_DATA( array )                      )
      FT_INVALID_OFFSET;

    gxv_odtect_add_range( table + GXV_KERN_FMT2_DATA( array ),
                          GXV_KERN_FMT2_DATA( offset_max[GXV_KERN_CLS_L] )
                            + GXV_KERN_FMT2_DATA( offset_max[GXV_KERN_CLS_R] )
                            - GXV_KERN_FMT2_DATA( array ),
                          "array", odtect );

    gxv_odtect_validate( odtect, valid );

    GXV_EXIT;
  }

  /* ============================= format 3 ============================== */

  static void
  gxv_kern_subtable_fmt3_validate( FT_Bytes       table,
                                   FT_Bytes       limit,
                                   GXV_Validator  valid )
  {
    FT_Bytes   p = table + GXV_KERN_SUBTABLE_HEADER_SIZE;
    FT_UShort  glyphCount;
    FT_Byte    kernValueCount;
    FT_Byte    leftClassCount;
    FT_Byte    rightClassCount;
    FT_Byte    flags;

    GXV_NAME_ENTER( "kern subtable format 3" );

    GXV_LIMIT_CHECK( 2 + 1 + 1 + 1 + 1 );
    glyphCount      = FT_NEXT_USHORT( p );
    kernValueCount  = FT_NEXT_BYTE( p );
    leftClassCount  = FT_NEXT_BYTE( p );
    rightClassCount = FT_NEXT_BYTE( p );
    flags           = FT_NEXT_BYTE( p );

    if ( valid->face->num_glyphs != glyphCount )
    {
      GXV_TRACE(( "maxGID=%d, but glyphCount=%d\n",
                  valid->face->num_glyphs, glyphCount ));
      if ( valid->root->level >= FT_VALIDATE_PARANOID )
        FT_INVALID_GLYPH_ID;
    }

    /*
     * just skip kernValue[kernValueCount]
     */
    GXV_LIMIT_CHECK( 2 * kernValueCount );
    p += 2 * kernValueCount;

    /*
     * check leftClass[gid] < leftClassCount
     */
    {
      FT_Byte  min, max;

      GXV_LIMIT_CHECK( glyphCount );
      gxv_array_getlimits_byte( p, p + glyphCount, &min, &max, valid );
      p += valid->subtable_length;

      if ( leftClassCount < max )
        FT_INVALID_DATA;
    }

    /*
     * check rightClass[gid] < rightClassCount
     */
    {
      FT_Byte  min, max;

      GXV_LIMIT_CHECK( glyphCount );
      gxv_array_getlimits_byte( p, p + glyphCount, &min, &max, valid );
      p += valid->subtable_length;

      if ( rightClassCount < max )
        FT_INVALID_DATA;
    }

    /*
     * check kernIndex[i, j] < kernValueCount
     */
    {
      FT_UShort  i, j;

      for ( i = 0; i < leftClassCount; i++ )
      {
        for ( j = 0; j < rightClassCount; j++ )
        {
          GXV_LIMIT_CHECK( 1 );
          if ( kernValueCount < FT_NEXT_BYTE( p ) )
            FT_INVALID_OFFSET;
        }
      }
    }

    valid->subtable_length = p - table;

    GXV_EXIT;
  }

  static FT_Bool
  gxv_kern_coverage_new_apple_validate( FT_UShort      coverage,
                                        FT_UShort*     format,
                                        GXV_Validator  valid )
  {
    /* new Apple-dialect */
    FT_Bool  kernVertical;
    FT_Bool  kernCrossStream;
    FT_Bool  kernVariation;

    FT_UNUSED( valid );

    /* reserved bits = 0 */
    if ( coverage & 0x1FFC )
      return 0;

    kernVertical    = FT_BOOL( ( coverage >> 15 ) & 1 );
    kernCrossStream = FT_BOOL( ( coverage >> 14 ) & 1 );
    kernVariation   = FT_BOOL( ( coverage >> 13 ) & 1 );

    *format = (FT_UShort)( coverage & 0x0003 );

    GXV_TRACE(( "new Apple-dialect: "
                "horizontal=%d, cross-stream=%d, variation=%d, format=%d\n",
                 !kernVertical, kernCrossStream, kernVariation, *format ));

    GXV_TRACE(( "kerning values in Apple format subtable are ignored\n" ));

    return 1;
  }

  static FT_Bool
  gxv_kern_coverage_classic_apple_validate( FT_UShort      coverage,
                                            FT_UShort*     format,
                                            GXV_Validator  valid )
  {
    /* classic Apple-dialect */
    FT_Bool  horizontal;
    FT_Bool  cross_stream;

    /* check expected flags, but don't check if MS-dialect is impossible */
    if ( !( coverage & 0xFD00 ) && KERN_ALLOWS_MS( valid ) )
      return 0;

    /* reserved bits = 0 */
    if ( coverage & 0x02FC )
      return 0;

    horizontal   = FT_BOOL( ( coverage >> 15 ) & 1 );
    cross_stream = FT_BOOL( ( coverage >> 13 ) & 1 );

    *format = (FT_UShort)( coverage & 0x0003 );

    GXV_TRACE(( "classic Apple-dialect: "
                "horizontal=%d, cross-stream=%d, format=%d\n",
                 horizontal, cross_stream, *format ));

    /* format 1 requires GX State Machine, too new for classic */
    if ( *format == 1 )
      return 0;

    GXV_TRACE(( "kerning values in Apple format subtable are ignored\n" ));

    return 1;
  }

  static FT_Bool
  gxv_kern_coverage_classic_microsoft_validate( FT_UShort      coverage,
                                                FT_UShort*     format,
                                                GXV_Validator  valid )
  {
    /* classic Microsoft-dialect */
    FT_Bool  horizontal;
    FT_Bool  minimum;
    FT_Bool  cross_stream;
    FT_Bool  override;

    FT_UNUSED( valid );

    /* reserved bits = 0 */
    if ( coverage & 0xFDF0 )
      return 0;

    horizontal   = FT_BOOL(   coverage        & 1 );
    minimum      = FT_BOOL( ( coverage >> 1 ) & 1 );
    cross_stream = FT_BOOL( ( coverage >> 2 ) & 1 );
    override     = FT_BOOL( ( coverage >> 3 ) & 1 );

    *format = (FT_UShort)( ( coverage >> 8 ) & 0x0003 );

    GXV_TRACE(( "classic Microsoft-dialect: "
                "horizontal=%d, minimum=%d, cross-stream=%d, "
                "override=%d, format=%d\n",
                horizontal, minimum, cross_stream, override, *format ));

    if ( *format == 2 )
      GXV_TRACE((
        "kerning values in Microsoft format 2 subtable are ignored\n" ));

    return 1;
  }

  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                            MAIN                               *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/

  static GXV_kern_Dialect
  gxv_kern_coverage_validate( FT_UShort      coverage,
                              FT_UShort*     format,
                              GXV_Validator  valid )
  {
    GXV_kern_Dialect  result = KERN_DIALECT_UNKNOWN;

    GXV_NAME_ENTER( "validating coverage" );

    GXV_TRACE(( "interprete coverage 0x%04x by Apple style\n", coverage ));

    if ( KERN_IS_NEW( valid ) )
    {
      if ( gxv_kern_coverage_new_apple_validate( coverage,
                                                 format,
                                                 valid ) )
      {
        result = KERN_DIALECT_APPLE;
        goto Exit;
      }
    }

    if ( KERN_IS_CLASSIC( valid ) && KERN_ALLOWS_APPLE( valid ) )
    {
      if ( gxv_kern_coverage_classic_apple_validate( coverage,
                                                     format,
                                                     valid ) )
      {
        result = KERN_DIALECT_APPLE;
        goto Exit;
      }
    }

    if ( KERN_IS_CLASSIC( valid ) && KERN_ALLOWS_MS( valid ) )
    {
      if ( gxv_kern_coverage_classic_microsoft_validate( coverage,
                                                         format,
                                                         valid ) )
      {
        result = KERN_DIALECT_MS;
        goto Exit;
      }
    }

    GXV_TRACE(( "cannot interprete coverage, broken kern subtable\n" ));

  Exit:
    GXV_EXIT;
    return result;
  }

  static void
  gxv_kern_subtable_validate( FT_Bytes       table,
                              FT_Bytes       limit,
                              GXV_Validator  valid )
  {
    FT_Bytes   p = table;
    FT_UShort  version = 0;    /* MS only: subtable version, unused */
    FT_ULong   length;         /* MS: 16bit, Apple: 32bit*/
    FT_UShort  coverage;
    FT_UShort  tupleIndex = 0; /* Apple only */
    FT_UShort  u16[2];
    FT_UShort  format = 255;   /* subtable format */

    GXV_NAME_ENTER( "kern subtable" );

    GXV_LIMIT_CHECK( 2 + 2 + 2 );
    u16[0]   = FT_NEXT_USHORT( p ); /* Apple: length_hi MS: version */
    u16[1]   = FT_NEXT_USHORT( p ); /* Apple: length_lo MS: length */
    coverage = FT_NEXT_USHORT( p );

    switch ( gxv_kern_coverage_validate( coverage, &format, valid ) )
    {
    case KERN_DIALECT_MS:
      version    = u16[0];
      length     = u16[1];
      tupleIndex = 0;
      GXV_TRACE(( "Subtable version = %d\n", version ));
      GXV_TRACE(( "Subtable length = %d\n", length ));
      break;

    case KERN_DIALECT_APPLE:
      version    = 0;
      length     = ( u16[0] << 16 ) + u16[1];
      tupleIndex = 0;
      GXV_TRACE(( "Subtable length = %d\n", length ));

      if ( KERN_IS_NEW( valid ) )
      {
        GXV_LIMIT_CHECK( 2 );
        tupleIndex = FT_NEXT_USHORT( p );
        GXV_TRACE(( "Subtable tupleIndex = %d\n", tupleIndex ));
      }
      break;

    default:
      length = u16[1];
      GXV_TRACE(( "cannot detect subtable dialect, "
                  "just skip %d byte\n", length ));
      goto Exit;
    }

    /* formats 1, 2, 3 require the position of the start of this subtable */
    if ( format == 0 )
      gxv_kern_subtable_fmt0_validate( table, table + length, valid );
    else if ( format == 1 )
      gxv_kern_subtable_fmt1_validate( table, table + length, valid );
    else if ( format == 2 )
      gxv_kern_subtable_fmt2_validate( table, table + length, valid );
    else if ( format == 3 )
      gxv_kern_subtable_fmt3_validate( table, table + length, valid );
    else
      FT_INVALID_DATA;

  Exit:
    valid->subtable_length = length;
    GXV_EXIT;
  }

  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                         kern TABLE                            *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/

  static void
  gxv_kern_validate_generic( FT_Bytes          table,
                             FT_Face           face,
                             FT_Bool           classic_only,
                             GXV_kern_Dialect  dialect_request,
                             FT_Validator      ftvalid )
  {
    GXV_ValidatorRec   validrec;
    GXV_Validator      valid = &validrec;

    GXV_kern_DataRec   kernrec;
    GXV_kern_Data      kern = &kernrec;

    FT_Bytes           p     = table;
    FT_Bytes           limit = 0;

    FT_ULong           nTables = 0;
    FT_UInt            i;

    valid->root       = ftvalid;
    valid->table_data = kern;
    valid->face       = face;

    FT_TRACE3(( "validating `kern' table\n" ));
    GXV_INIT;
    KERN_DIALECT( valid ) = dialect_request;

    GXV_LIMIT_CHECK( 2 );
    GXV_KERN_DATA( version ) = (GXV_kern_Version)FT_NEXT_USHORT( p );
    GXV_TRACE(( "version 0x%04x (higher 16bit)\n",
                GXV_KERN_DATA( version ) ));

    if ( 0x0001 < GXV_KERN_DATA( version ) )
      FT_INVALID_FORMAT;
    else if ( KERN_IS_CLASSIC( valid ) )
    {
      GXV_LIMIT_CHECK( 2 );
      nTables = FT_NEXT_USHORT( p );
    }
    else if ( KERN_IS_NEW( valid ) )
    {
      if ( classic_only )
        FT_INVALID_FORMAT;

      if ( 0x0000 != FT_NEXT_USHORT( p ) )
        FT_INVALID_FORMAT;

      GXV_LIMIT_CHECK( 4 );
      nTables = FT_NEXT_ULONG( p );
    }

    for ( i = 0; i < nTables; i++ )
    {
      GXV_TRACE(( "validating subtable %d/%d\n", i, nTables ));
      /* p should be 32bit-aligned? */
      gxv_kern_subtable_validate( p, 0, valid );
      p += valid->subtable_length;
    }

    FT_TRACE4(( "\n" ));
  }

  FT_LOCAL_DEF( void )
  gxv_kern_validate( FT_Bytes      table,
                     FT_Face       face,
                     FT_Validator  ftvalid )
  {
    gxv_kern_validate_generic( table, face, 0, KERN_DIALECT_ANY, ftvalid );
  }

  FT_LOCAL_DEF( void )
  gxv_kern_validate_classic( FT_Bytes      table,
                             FT_Face       face,
                             FT_Int        dialect_flags,
                             FT_Validator  ftvalid )
  {
    GXV_kern_Dialect  dialect_request;

    dialect_request = (GXV_kern_Dialect)dialect_flags;
    gxv_kern_validate_generic( table, face, 1, dialect_request, ftvalid );
  }

/* END */


code:
/***************************************************************************/
/*                                                                         */
/*  gxvlcar.c                                                              */
/*                                                                         */
/*    TrueTypeGX/AAT lcar table validation (body).                         */
/*                                                                         */
/*  Copyright 2004, 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/*  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.                                        */
/*                                                                         */
/***************************************************************************/

/***************************************************************************/
/*                                                                         */
/* gxvalid is derived from both gxlayout module and otvalid module.        */
/* Development of gxlayout is supported by the Information-technology      */
/* Promotion Agency(IPA), Japan.                                           */
/*                                                                         */
/***************************************************************************/

#include "gxvalid.h"
#include "gxvcommn.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_gxvlcar

  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                      Data and Types                           *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/

  typedef struct  GXV_lcar_DataRec_
  {
    FT_UShort  format;

  } GXV_lcar_DataRec, *GXV_lcar_Data;

#define GXV_LCAR_DATA( FIELD )  GXV_TABLE_DATA( lcar, FIELD )

  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                      UTILITY FUNCTIONS                        *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/

  static void
  gxv_lcar_partial_validate( FT_UShort      partial,
                             FT_UShort      glyph,
                             GXV_Validator  valid )
  {
    GXV_NAME_ENTER( "partial" );

    if ( GXV_LCAR_DATA( format ) != 1 )
      goto Exit;

    gxv_ctlPoint_validate( glyph, partial, valid );

  Exit:
    GXV_EXIT;
  }

  static void
  gxv_lcar_LookupValue_validate( FT_UShort            glyph,
                                 GXV_LookupValueCPtr  value_p,
                                 GXV_Validator        valid )
  {
    FT_Bytes   p     = valid->root->base + value_p->u;
    FT_Bytes   limit = valid->root->limit;
    FT_UShort  count;
    FT_Short   partial;
    FT_UShort  i;

    GXV_NAME_ENTER( "element in lookupTable" );

    GXV_LIMIT_CHECK( 2 );
    count = FT_NEXT_USHORT( p );

    GXV_LIMIT_CHECK( 2 * count );
    for ( i = 0; i < count; i++ )
    {
      partial = FT_NEXT_SHORT( p );
      gxv_lcar_partial_validate( partial, glyph, valid );
    }

    GXV_EXIT;
  }

  /*
    +------ lcar --------------------+
    |                                |
    |      +===============+         |
    |      | looup header  |         |
    |      +===============+         |
    |      | BinSrchHeader |         |
    |      +===============+         |
    |      | lastGlyph[0]  |         |
    |      +---------------+         |
    |      | firstGlyph[0] |         |  head of lcar sfnt table
    |      +---------------+         |             +
    |      | offset[0]     |    ->   |          offset            [byte]
    |      +===============+         |             +
    |      | lastGlyph[1]  |         | (glyphID - firstGlyph) * 2 [byte]
    |      +---------------+         |
    |      | firstGlyph[1] |         |
    |      +---------------+         |
    |      | offset[1]     |         |
    |      +===============+         |
    |                                |
    |       ....                     |
    |                                |
    |      16bit value array         |
    |      +===============+         |
    +------|     value     | <-------+
    |       ....
    |
    |
    |
    |
    |
    +---->  lcar values...handled by lcar callback function
  */

  static GXV_LookupValueDesc
  gxv_lcar_LookupFmt4_transit( FT_UShort            relative_gindex,
                               GXV_LookupValueCPtr  base_value_p,
                               FT_Bytes             lookuptbl_limit,
                               GXV_Validator        valid )
  {
    FT_Bytes             p;
    FT_Bytes             limit;
    FT_UShort            offset;
    GXV_LookupValueDesc  value;

    FT_UNUSED( lookuptbl_limit );

    /* XXX: check range? */
    offset = (FT_UShort)( base_value_p->u +
                          relative_gindex * sizeof ( FT_UShort ) );
    p      = valid->root->base + offset;
    limit  = valid->root->limit;

    GXV_LIMIT_CHECK ( 2 );
    value.u = FT_NEXT_USHORT( p );

    return value;
  }

  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                          lcar TABLE                           *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/

  FT_LOCAL_DEF( void )
  gxv_lcar_validate( FT_Bytes      table,
                     FT_Face       face,
                     FT_Validator  ftvalid )
  {
    FT_Bytes          p     = table;
    FT_Bytes          limit = 0;
    GXV_ValidatorRec  validrec;
    GXV_Validator     valid = &validrec;

    GXV_lcar_DataRec  lcarrec;
    GXV_lcar_Data     lcar = &lcarrec;

    FT_Fixed          version;

    valid->root       = ftvalid;
    valid->table_data = lcar;
    valid->face       = face;

    FT_TRACE3(( "validating `lcar' table\n" ));
    GXV_INIT;

    GXV_LIMIT_CHECK( 4 + 2 );
    version = FT_NEXT_ULONG( p );
    GXV_LCAR_DATA( format ) = FT_NEXT_USHORT( p );

    if ( version != 0x00010000UL)
      FT_INVALID_FORMAT;

    if ( GXV_LCAR_DATA( format ) > 1 )
      FT_INVALID_FORMAT;

    valid->lookupval_sign   = GXV_LOOKUPVALUE_UNSIGNED;
    valid->lookupval_func   = gxv_lcar_LookupValue_validate;
    valid->lookupfmt4_trans = gxv_lcar_LookupFmt4_transit;
    gxv_LookupTable_validate( p, limit, valid );

    FT_TRACE4(( "\n" ));
  }

/* END */

license: mit


code:
/***************************************************************************/
/*                                                                         */
/*  gxvmod.c                                                               */
/*                                                                         */
/*    FreeType's TrueTypeGX/AAT validation module implementation (body).   */
/*                                                                         */
/*  Copyright 2004, 2005, 2006                                             */
/*  by suzuki toshiya, Masatake YAMATO, Red Hat K.K.,                      */
/*  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.                                        */
/*                                                                         */
/***************************************************************************/

/***************************************************************************/
/*                                                                         */
/* gxvalid is derived from both gxlayout module and otvalid module.        */
/* Development of gxlayout is supported by the Information-technology      */
/* Promotion Agency(IPA), Japan.                                           */
/*                                                                         */
/***************************************************************************/

#include <ft2build.h>
#include FT_TRUETYPE_TABLES_H
#include FT_TRUETYPE_TAGS_H
#include FT_GX_VALIDATE_H
#include FT_INTERNAL_OBJECTS_H
#include FT_SERVICE_GX_VALIDATE_H

#include "gxvmod.h"
#include "gxvalid.h"
#include "gxvcommn.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_gxvmodule

  static FT_Error
  gxv_load_table( FT_Face             face,
                  FT_Tag              tag,
                  FT_Byte* volatile*  table,
                  FT_ULong*           table_len )
  {
    FT_Error   error;
    FT_Memory  memory = FT_FACE_MEMORY( face );

    error = FT_Load_Sfnt_Table( face, tag, 0, NULL, table_len );
    if ( error == GXV_Err_Table_Missing )
      return GXV_Err_Ok;
    if ( error )
      goto Exit;

    if ( FT_ALLOC( *table, *table_len ) )
      goto Exit;

    error = FT_Load_Sfnt_Table( face, tag, 0, *table, table_len );

  Exit:
    return error;
  }

#define GXV_TABLE_DECL( _sfnt )                     \
          FT_Byte* volatile  _sfnt          = NULL; \
          FT_ULong            len_ ## _sfnt = 0

#define GXV_TABLE_LOAD( _sfnt )                                     \
          if ( ( FT_VALIDATE_ ## _sfnt ## _INDEX < table_count ) && \
               ( gx_flags & FT_VALIDATE_ ## _sfnt )            )    \
          {                                                         \
            error = gxv_load_table( face, TTAG_ ## _sfnt,           \
                                    &_sfnt, &len_ ## _sfnt );       \
            if ( error )                                            \
              goto Exit;                                            \
          }

#define GXV_TABLE_VALIDATE( _sfnt )                                  \
          if ( _sfnt )                                               \
          {                                                          \
            ft_validator_init( &valid, _sfnt, _sfnt + len_ ## _sfnt, \
                               FT_VALIDATE_DEFAULT );                \
            if ( ft_setjmp( valid.jump_buffer ) == 0 )               \
              gxv_ ## _sfnt ## _validate( _sfnt, face, &valid );     \
            error = valid.error;                                     \
            if ( error )                                             \
              goto Exit;                                             \
          }

#define GXV_TABLE_SET( _sfnt )                                        \
          if ( FT_VALIDATE_ ## _sfnt ## _INDEX < table_count )        \
            tables[FT_VALIDATE_ ## _sfnt ## _INDEX] = (FT_Bytes)_sfnt

  static FT_Error
  gxv_validate( FT_Face   face,
                FT_UInt   gx_flags,
                FT_Bytes  tables[FT_VALIDATE_GX_LENGTH],
                FT_UInt   table_count )
  {
    FT_Memory volatile        memory = FT_FACE_MEMORY( face );

    FT_Error                  error = GXV_Err_Ok;
    FT_ValidatorRec volatile  valid;

    FT_UInt  i;

    GXV_TABLE_DECL( feat );
    GXV_TABLE_DECL( bsln );
    GXV_TABLE_DECL( trak );
    GXV_TABLE_DECL( just );
    GXV_TABLE_DECL( mort );
    GXV_TABLE_DECL( morx );
    GXV_TABLE_DECL( kern );
    GXV_TABLE_DECL( opbd );
    GXV_TABLE_DECL( prop );
    GXV_TABLE_DECL( lcar );

    for ( i = 0; i < table_count; i++ )
      tables[i] = 0;

    /* load tables */
    GXV_TABLE_LOAD( feat );
    GXV_TABLE_LOAD( bsln );
    GXV_TABLE_LOAD( trak );
    GXV_TABLE_LOAD( just );
    GXV_TABLE_LOAD( mort );
    GXV_TABLE_LOAD( morx );
    GXV_TABLE_LOAD( kern );
    GXV_TABLE_LOAD( opbd );
    GXV_TABLE_LOAD( prop );
    GXV_TABLE_LOAD( lcar );

    /* validate tables */
    GXV_TABLE_VALIDATE( feat );
    GXV_TABLE_VALIDATE( bsln );
    GXV_TABLE_VALIDATE( trak );
    GXV_TABLE_VALIDATE( just );
    GXV_TABLE_VALIDATE( mort );
    GXV_TABLE_VALIDATE( morx );
    GXV_TABLE_VALIDATE( kern );
    GXV_TABLE_VALIDATE( opbd );
    GXV_TABLE_VALIDATE( prop );
    GXV_TABLE_VALIDATE( lcar );

    /* Set results */
    GXV_TABLE_SET( feat );
    GXV_TABLE_SET( mort );
    GXV_TABLE_SET( morx );
    GXV_TABLE_SET( bsln );
    GXV_TABLE_SET( just );
    GXV_TABLE_SET( kern );
    GXV_TABLE_SET( opbd );
    GXV_TABLE_SET( trak );
    GXV_TABLE_SET( prop );
    GXV_TABLE_SET( lcar );

  Exit:
    if ( error )
    {
      FT_FREE( feat );
      FT_FREE( bsln );
      FT_FREE( trak );
      FT_FREE( just );
      FT_FREE( mort );
      FT_FREE( morx );
      FT_FREE( kern );
      FT_FREE( opbd );
      FT_FREE( prop );
      FT_FREE( lcar );
    }

    return error;
  }

  static FT_Error
  classic_kern_validate( FT_Face    face,
                         FT_UInt    ckern_flags,
                         FT_Bytes*  ckern_table )
  {
    FT_Memory volatile        memory = FT_FACE_MEMORY( face );

    FT_Byte* volatile         ckern     = NULL;
    FT_ULong                  len_ckern = 0;

    /* without volatile on `error' GCC 4.1.1. emits:                         */
    /*  warning: variable 'error' might be clobbered by 'longjmp' or 'vfork' */
    /* this warning seems spurious but ---                                   */
    FT_Error volatile         error = GXV_Err_Ok;
    FT_ValidatorRec volatile  valid;

    *ckern_table = NULL;

    error = gxv_load_table( face, TTAG_kern, &ckern, &len_ckern );
    if ( error )
      goto Exit;

    if ( ckern )
    {
      ft_validator_init( &valid, ckern, ckern + len_ckern,
                         FT_VALIDATE_DEFAULT );
      if ( ft_setjmp( valid.jump_buffer ) == 0 )
        gxv_kern_validate_classic( ckern, face,
                                   ckern_flags & FT_VALIDATE_CKERN, &valid );
      error = valid.error;
      if ( error )
        goto Exit;
    }

    *ckern_table = ckern;

  Exit:
    if ( error )
      FT_FREE( ckern );

    return error;
  }

  static
  const FT_Service_GXvalidateRec  gxvalid_interface =
  {
    gxv_validate
  };

  static
  const FT_Service_CKERNvalidateRec  ckernvalid_interface =
  {
    classic_kern_validate
  };

  static
  const FT_ServiceDescRec  gxvalid_services[] =
  {
    { FT_SERVICE_ID_GX_VALIDATE,          &gxvalid_interface },
    { FT_SERVICE_ID_CLASSICKERN_VALIDATE, &ckernvalid_interface },
    { NULL, NULL }
  };

  static FT_Pointer
  gxvalid_get_service( FT_Module    module,
                       const char*  service_id )
  {
    FT_UNUSED( module );

    return ft_service_list_lookup( gxvalid_services, service_id );
  }

  FT_CALLBACK_TABLE_DEF
  const FT_Module_Class  gxv_module_class =
  {
    0,
    sizeof( FT_ModuleRec ),
    "gxvalid",
    0x10000L,
    0x20000L,

    0,              /* module-specific interface */

    (FT_Module_Constructor)0,
    (FT_Module_Destructor) 0,
    (FT_Module_Requester)  gxvalid_get_service
  };

/* END */


code:
/***************************************************************************/
/*                                                                         */
/*  gxvmod.h                                                               */
/*                                                                         */
/*    FreeType's TrueTypeGX/AAT validation module implementation           */
/*    (specification).                                                     */
/*                                                                         */
/*  Copyright 2004, 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/*  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.                                        */
/*                                                                         */
/***************************************************************************/

/***************************************************************************/
/*                                                                         */
/* gxvalid is derived from both gxlayout module and otvalid module.        */
/* Development of gxlayout is supported by the Information-technology      */
/* Promotion Agency(IPA), Japan.                                           */
/*                                                                         */
/***************************************************************************/

#ifndef __GXVMOD_H__
#define __GXVMOD_H__

#include <ft2build.h>
#include FT_MODULE_H

FT_BEGIN_HEADER

#ifdef FT_CONFIG_OPTION_PIC
#error "this module does not support PIC yet"
#endif

  FT_EXPORT_VAR( const FT_Module_Class )  gxv_module_class;

FT_END_HEADER

#endif /* __GXVMOD_H__ */

/* END */


code:
/***************************************************************************/
/*                                                                         */
/*  gxvmort.c                                                              */
/*                                                                         */
/*    TrueTypeGX/AAT mort table validation (body).                         */
/*                                                                         */
/*  Copyright 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K.,       */
/*  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.                                        */
/*                                                                         */
/***************************************************************************/

/***************************************************************************/
/*                                                                         */
/* gxvalid is derived from both gxlayout module and otvalid module.        */
/* Development of gxlayout is supported by the Information-technology      */
/* Promotion Agency(IPA), Japan.                                           */
/*                                                                         */
/***************************************************************************/

#include "gxvmort.h"
#include "gxvfeat.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_gxvmort

  static void
  gxv_mort_feature_validate( GXV_mort_feature  f,
                             GXV_Validator     valid )
  {
    if ( f->featureType >= gxv_feat_registry_length )
    {
      GXV_TRACE(( "featureType %d is out of registered range, "
                  "setting %d is unchecked\n",
                  f->featureType, f->featureSetting ));
      if ( valid->root->level >= FT_VALIDATE_PARANOID )
        FT_INVALID_DATA;
    }
    else if ( !gxv_feat_registry[f->featureType].existence )
    {
      GXV_TRACE(( "featureType %d is within registered area "
                  "but undefined, setting %d is unchecked\n",
                  f->featureType, f->featureSetting ));
      if ( valid->root->level >= FT_VALIDATE_PARANOID )
        FT_INVALID_DATA;
    }
    else
    {
      FT_Byte  nSettings_max;

      /* nSettings in gxvfeat.c is halved for exclusive on/off settings */
      nSettings_max = gxv_feat_registry[f->featureType].nSettings;
      if ( gxv_feat_registry[f->featureType].exclusive )
        nSettings_max = (FT_Byte)( 2 * nSettings_max );

      GXV_TRACE(( "featureType %d is registered", f->featureType ));
      GXV_TRACE(( "setting %d", f->featureSetting ));

      if ( f->featureSetting > nSettings_max )
      {
        GXV_TRACE(( "out of defined range %d", nSettings_max ));
        if ( valid->root->level >= FT_VALIDATE_PARANOID )
          FT_INVALID_DATA;
      }
      GXV_TRACE(( "\n" ));
    }

    /* TODO: enableFlags must be unique value in specified chain?  */
  }

  /*
   * nFeatureFlags is typed to FT_ULong to accept that in
   * mort (typed FT_UShort) and morx (typed FT_ULong).
   */
  FT_LOCAL_DEF( void )
  gxv_mort_featurearray_validate( FT_Bytes       table,
                                  FT_Bytes       limit,
                                  FT_ULong       nFeatureFlags,
                                  GXV_Validator  valid )
  {
    FT_Bytes  p = table;
    FT_ULong  i;

    GXV_mort_featureRec  f = GXV_MORT_FEATURE_OFF;

    GXV_NAME_ENTER( "mort feature list" );
    for ( i = 0; i < nFeatureFlags; i++ )
    {
      GXV_LIMIT_CHECK( 2 + 2 + 4 + 4 );
      f.featureType    = FT_NEXT_USHORT( p );
      f.featureSetting = FT_NEXT_USHORT( p );
      f.enableFlags    = FT_NEXT_ULONG( p );
      f.disableFlags   = FT_NEXT_ULONG( p );

      gxv_mort_feature_validate( &f, valid );
    }

    if ( !IS_GXV_MORT_FEATURE_OFF( f ) )
      FT_INVALID_DATA;

    valid->subtable_length = p - table;
    GXV_EXIT;
  }

  FT_LOCAL_DEF( void )
  gxv_mort_coverage_validate( FT_UShort      coverage,
                              GXV_Validator  valid )
  {
    FT_UNUSED( valid );

    if ( coverage & 0x8000U )
      GXV_TRACE(( " this subtable is for vertical text only\n" ));
    else
      GXV_TRACE(( " this subtable is for horizontal text only\n" ));

    if ( coverage & 0x4000 )
      GXV_TRACE(( " this subtable is applied to glyph array "
                  "in descending order\n" ));
    else
      GXV_TRACE(( " this subtable is applied to glyph array "
                  "in ascending order\n" ));

    if ( coverage & 0x2000 )
      GXV_TRACE(( " this subtable is forcibly applied to "
                  "vertical/horizontal text\n" ));

    if ( coverage & 0x1FF8 )
      GXV_TRACE(( " coverage has non-zero bits in reserved area\n" ));
  }

  static void
  gxv_mort_subtables_validate( FT_Bytes       table,
                               FT_Bytes       limit,
                               FT_UShort      nSubtables,
                               GXV_Validator  valid )
  {
    FT_Bytes  p = table;

    GXV_Validate_Func fmt_funcs_table[] =
    {
      gxv_mort_subtable_type0_validate, /* 0 */
      gxv_mort_subtable_type1_validate, /* 1 */
      gxv_mort_subtable_type2_validate, /* 2 */
      NULL,                             /* 3 */
      gxv_mort_subtable_type4_validate, /* 4 */
      gxv_mort_subtable_type5_validate, /* 5 */

    };

    GXV_Validate_Func  func;
    FT_UShort          i;

    GXV_NAME_ENTER( "subtables in a chain" );

    for ( i = 0; i < nSubtables; i++ )
    {
      FT_UShort  length;
      FT_UShort  coverage;
      FT_ULong   subFeatureFlags;
      FT_UInt    type;
      FT_UInt    rest;

      GXV_LIMIT_CHECK( 2 + 2 + 4 );
      length          = FT_NEXT_USHORT( p );
      coverage        = FT_NEXT_USHORT( p );
      subFeatureFlags = FT_NEXT_ULONG( p );

      GXV_TRACE(( "validating chain subtable %d/%d (%d bytes)\n",
                  i + 1, nSubtables, length ));
      type = coverage & 0x0007;
      rest = length - ( 2 + 2 + 4 );

      GXV_LIMIT_CHECK( rest );
      gxv_mort_coverage_validate( coverage, valid );

      if ( type > 5 )
        FT_INVALID_FORMAT;

      func = fmt_funcs_table[type];
      if ( func == NULL )
        GXV_TRACE(( "morx type %d is reserved\n", type ));

      func( p, p + rest, valid );

      p += rest;
    }

    valid->subtable_length = p - table;

    GXV_EXIT;
  }

  static void
  gxv_mort_chain_validate( FT_Bytes       table,
                           FT_Bytes       limit,
                           GXV_Validator  valid )
  {
    FT_Bytes   p = table;
    FT_ULong   defaultFlags;
    FT_ULong   chainLength;
    FT_UShort  nFeatureFlags;
    FT_UShort  nSubtables;

    GXV_NAME_ENTER( "mort chain header" );

    GXV_LIMIT_CHECK( 4 + 4 + 2 + 2 );
    defaultFlags  = FT_NEXT_ULONG( p );
    chainLength   = FT_NEXT_ULONG( p );
    nFeatureFlags = FT_NEXT_USHORT( p );
    nSubtables    = FT_NEXT_USHORT( p );

    gxv_mort_featurearray_validate( p, table + chainLength,
                                    nFeatureFlags, valid );
    p += valid->subtable_length;
    gxv_mort_subtables_validate( p, table + chainLength, nSubtables, valid );
    valid->subtable_length = chainLength;

    GXV_EXIT;
  }

  FT_LOCAL_DEF( void )
  gxv_mort_validate( FT_Bytes      table,
                     FT_Face       face,
                     FT_Validator  ftvalid )
  {
    GXV_ValidatorRec  validrec;
    GXV_Validator     valid = &validrec;
    FT_Bytes          p     = table;
    FT_Bytes          limit = 0;
    FT_ULong          version;
    FT_ULong          nChains;
    FT_ULong          i;

    valid->root = ftvalid;
    valid->face = face;
    limit       = valid->root->limit;

    FT_TRACE3(( "validating `mort' table\n" ));
    GXV_INIT;

    GXV_LIMIT_CHECK( 4 + 4 );
    version = FT_NEXT_ULONG( p );
    nChains = FT_NEXT_ULONG( p );

    if (version != 0x00010000UL)
      FT_INVALID_FORMAT;

    for ( i = 0; i < nChains; i++ )
    {
      GXV_TRACE(( "validating chain %d/%d\n", i + 1, nChains ));
      GXV_32BIT_ALIGNMENT_VALIDATE( p - table );
      gxv_mort_chain_validate( p, limit, valid );
      p += valid->subtable_length;
    }

    FT_TRACE4(( "\n" ));
  }

/* END */


code:
/***************************************************************************/
/*                                                                         */
/*  gxvmort.h                                                              */
/*                                                                         */
/*    TrueTypeGX/AAT common definition for mort table (specification).     */
/*                                                                         */
/*  Copyright 2004, 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/*  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.                                        */
/*                                                                         */
/***************************************************************************/

/***************************************************************************/
/*                                                                         */
/* gxvalid is derived from both gxlayout module and otvalid module.        */
/* Development of gxlayout is supported by the Information-technology      */
/* Promotion Agency(IPA), Japan.                                           */
/*                                                                         */
/***************************************************************************/

#ifndef __GXVMORT_H__
#define __GXVMORT_H__

#include "gxvalid.h"
#include "gxvcommn.h"

#include FT_SFNT_NAMES_H

  typedef struct  GXV_mort_featureRec_
  {
    FT_UShort  featureType;
    FT_UShort  featureSetting;
    FT_ULong   enableFlags;
    FT_ULong   disableFlags;

  } GXV_mort_featureRec, *GXV_mort_feature;

#define GXV_MORT_FEATURE_OFF  {0, 1, 0x00000000UL, 0x00000000UL}

#define IS_GXV_MORT_FEATURE_OFF( f )              \
          ( (f).featureType    == 0            || \
            (f).featureSetting == 1            || \
            (f).enableFlags    == 0x00000000UL || \
            (f).disableFlags   == 0x00000000UL )

  FT_LOCAL( void )
  gxv_mort_featurearray_validate( FT_Bytes       table,
                                  FT_Bytes       limit,
                                  FT_ULong       nFeatureFlags,
                                  GXV_Validator  valid );

  FT_LOCAL( void )
  gxv_mort_coverage_validate( FT_UShort      coverage,
                              GXV_Validator  valid );

  FT_LOCAL( void )
  gxv_mort_subtable_type0_validate( FT_Bytes       table,
                                    FT_Bytes       limit,
                                    GXV_Validator  valid );

  FT_LOCAL( void )
  gxv_mort_subtable_type1_validate( FT_Bytes       table,
                                    FT_Bytes       limit,
                                    GXV_Validator  valid );

  FT_LOCAL( void )
  gxv_mort_subtable_type2_validate( FT_Bytes       table,
                                    FT_Bytes       limit,
                                    GXV_Validator  valid );

  FT_LOCAL( void )
  gxv_mort_subtable_type4_validate( FT_Bytes       table,
                                    FT_Bytes       limit,
                                    GXV_Validator  valid );

  FT_LOCAL( void )
  gxv_mort_subtable_type5_validate( FT_Bytes       table,
                                    FT_Bytes       limit,
                                    GXV_Validator  valid );

#endif /* __GXVMORT_H__ */

/* END */


code:
/***************************************************************************/
/*                                                                         */
/*  gxvmort0.c                                                             */
/*                                                                         */
/*    TrueTypeGX/AAT mort table validation                                 */
/*    body for type0 (Indic Script Rearrangement) subtable.                */
/*                                                                         */
/*  Copyright 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K.,       */
/*  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.                                        */
/*                                                                         */
/***************************************************************************/

/***************************************************************************/
/*                                                                         */
/* gxvalid is derived from both gxlayout module and otvalid module.        */
/* Development of gxlayout is supported by the Information-technology      */
/* Promotion Agency(IPA), Japan.                                           */
/*                                                                         */
/***************************************************************************/

#include "gxvmort.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_gxvmort

  static const char* GXV_Mort_IndicScript_Msg[] =
  {
    "no change",
    "Ax => xA",
    "xD => Dx",
    "AxD => DxA",
    "ABx => xAB",
    "ABx => xBA",
    "xCD => CDx",
    "xCD => DCx",
    "AxCD => CDxA",
    "AxCD => DCxA",
    "ABxD => DxAB",
    "ABxD => DxBA",
    "ABxCD => CDxAB",
    "ABxCD => CDxBA",
    "ABxCD => DCxAB",
    "ABxCD => DCxBA",

  };

  static void
  gxv_mort_subtable_type0_entry_validate(
    FT_Byte                         state,
    FT_UShort                       flags,
    GXV_StateTable_GlyphOffsetCPtr  glyphOffset_p,
    FT_Bytes                        table,
    FT_Bytes                        limit,
    GXV_Validator                   valid )
  {
    FT_UShort  markFirst;
    FT_UShort  dontAdvance;
    FT_UShort  markLast;
    FT_UShort  reserved;
    FT_UShort  verb = 0;

    FT_UNUSED( state );
    FT_UNUSED( table );
    FT_UNUSED( limit );

    FT_UNUSED( GXV_Mort_IndicScript_Msg[verb] ); /* for the non-debugging */
    FT_UNUSED( glyphOffset_p );                  /* case                  */

    markFirst   = (FT_UShort)( ( flags >> 15 ) & 1 );
    dontAdvance = (FT_UShort)( ( flags >> 14 ) & 1 );
    markLast    = (FT_UShort)( ( flags >> 13 ) & 1 );

    reserved = (FT_UShort)( flags & 0x1FF0 );
    verb     = (FT_UShort)( flags & 0x000F );

    GXV_TRACE(( "  IndicScript MorphRule for glyphOffset 0x%04x",
                glyphOffset_p->u ));
    GXV_TRACE(( " markFirst=%01d", markFirst ));
    GXV_TRACE(( " dontAdvance=%01d", dontAdvance ));
    GXV_TRACE(( " markLast=%01d", markLast ));
    GXV_TRACE(( " %02d", verb ));
    GXV_TRACE(( " %s\n", GXV_Mort_IndicScript_Msg[verb] ));

    if ( 0 < reserved )
    {
      GXV_TRACE(( " non-zero bits found in reserved range\n" ));
      FT_INVALID_DATA;
    }
    else
      GXV_TRACE(( "\n" ));
  }

  FT_LOCAL_DEF( void )
  gxv_mort_subtable_type0_validate( FT_Bytes       table,
                                    FT_Bytes       limit,
                                    GXV_Validator  valid )
  {
    FT_Bytes  p = table;

    GXV_NAME_ENTER(
      "mort chain subtable type0 (Indic-Script Rearrangement)" );

    GXV_LIMIT_CHECK( GXV_STATETABLE_HEADER_SIZE );

    valid->statetable.optdata               = NULL;
    valid->statetable.optdata_load_func     = NULL;
    valid->statetable.subtable_setup_func   = NULL;
    valid->statetable.entry_glyphoffset_fmt = GXV_GLYPHOFFSET_NONE;
    valid->statetable.entry_validate_func =
      gxv_mort_subtable_type0_entry_validate;

    gxv_StateTable_validate( p, limit, valid );

    GXV_EXIT;
  }

/* END */


code:
/***************************************************************************/
/*                                                                         */
/*  gxvmort1.c                                                             */
/*                                                                         */
/*    TrueTypeGX/AAT mort table validation                                 */
/*    body for type1 (Contextual Substitution) subtable.                   */
/*                                                                         */
/*  Copyright 2005, 2007 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
/*  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.                                        */
/*                                                                         */
/***************************************************************************/

/***************************************************************************/
/*                                                                         */
/* gxvalid is derived from both gxlayout module and otvalid module.        */
/* Development of gxlayout is supported by the Information-technology      */
/* Promotion Agency(IPA), Japan.                                           */
/*                                                                         */
/***************************************************************************/

#include "gxvmort.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_gxvmort

  typedef struct  GXV_mort_subtable_type1_StateOptRec_
  {
    FT_UShort  substitutionTable;
    FT_UShort  substitutionTable_length;

  }  GXV_mort_subtable_type1_StateOptRec,
    *GXV_mort_subtable_type1_StateOptRecData;

#define GXV_MORT_SUBTABLE_TYPE1_HEADER_SIZE \
          ( GXV_STATETABLE_HEADER_SIZE + 2 )

  static void
  gxv_mort_subtable_type1_substitutionTable_load( FT_Bytes       table,
                                                  FT_Bytes       limit,
                                                  GXV_Validator  valid )
  {
    FT_Bytes  p = table;

    GXV_mort_subtable_type1_StateOptRecData  optdata =
      (GXV_mort_subtable_type1_StateOptRecData)valid->statetable.optdata;

    GXV_LIMIT_CHECK( 2 );
    optdata->substitutionTable = FT_NEXT_USHORT( p );
  }

  static void
  gxv_mort_subtable_type1_subtable_setup( FT_UShort      table_size,
                                          FT_UShort      classTable,
                                          FT_UShort      stateArray,
                                          FT_UShort      entryTable,
                                          FT_UShort*     classTable_length_p,
                                          FT_UShort*     stateArray_length_p,
                                          FT_UShort*     entryTable_length_p,
                                          GXV_Validator  valid )
  {
    FT_UShort  o[4];
    FT_UShort  *l[4];
    FT_UShort  buff[5];

    GXV_mort_subtable_type1_StateOptRecData  optdata =
      (GXV_mort_subtable_type1_StateOptRecData)valid->statetable.optdata;

    o[0] = classTable;
    o[1] = stateArray;
    o[2] = entryTable;
    o[3] = optdata->substitutionTable;
    l[0] = classTable_length_p;
    l[1] = stateArray_length_p;
    l[2] = entryTable_length_p;
    l[3] = &( optdata->substitutionTable_length );

    gxv_set_length_by_ushort_offset( o, l, buff, 4, table_size, valid );
  }

  static void
  gxv_mort_subtable_type1_offset_to_subst_validate(
    FT_Short          wordOffset,
    const FT_String*  tag,
    FT_Byte           state,
    GXV_Validator     valid )
  {
    FT_UShort  substTable;
    FT_UShort  substTable_limit;
    FT_UShort  min_gid;
    FT_UShort  max_gid;

    FT_UNUSED( tag );
    FT_UNUSED( state );

    substTable =
      ((GXV_mort_subtable_type1_StateOptRec *)
       (valid->statetable.optdata))->substitutionTable;
    substTable_limit =
      (FT_UShort)( substTable +
                   ((GXV_mort_subtable_type1_StateOptRec *)
                    (valid->statetable.optdata))->substitutionTable_length );

    min_gid = (FT_UShort)( ( substTable       - wordOffset * 2 ) / 2 );
    max_gid = (FT_UShort)( ( substTable_limit - wordOffset * 2 ) / 2 );
    max_gid = (FT_UShort)( FT_MAX( max_gid, valid->face->num_glyphs ) );

    /* XXX: check range? */

    /* TODO: min_gid & max_gid comparison with ClassTable contents */
  }

  static void
  gxv_mort_subtable_type1_entry_validate(
    FT_Byte                         state,
    FT_UShort                       flags,
    GXV_StateTable_GlyphOffsetCPtr  glyphOffset_p,
    FT_Bytes                        table,
    FT_Bytes                        limit,
    GXV_Validator                   valid )
  {
    FT_UShort  setMark;
    FT_UShort  dontAdvance;
    FT_UShort  reserved;
    FT_Short   markOffset;
    FT_Short   currentOffset;

    FT_UNUSED( table );
    FT_UNUSED( limit );

    setMark       = (FT_UShort)(   flags >> 15            );
    dontAdvance   = (FT_UShort)( ( flags >> 14 ) & 1      );
    reserved      = (FT_Short)(    flags         & 0x3FFF );

    markOffset    = (FT_Short)( glyphOffset_p->ul >> 16 );
    currentOffset = (FT_Short)( glyphOffset_p->ul       );

    if ( 0 < reserved )
    {
      GXV_TRACE(( " non-zero bits found in reserved range\n" ));
      if ( valid->root->level >= FT_VALIDATE_PARANOID )
        FT_INVALID_DATA;
    }

    gxv_mort_subtable_type1_offset_to_subst_validate( markOffset,
                                                      "markOffset",
                                                      state,
                                                      valid );

    gxv_mort_subtable_type1_offset_to_subst_validate( currentOffset,
                                                      "currentOffset",
                                                      state,
                                                      valid );
  }

  static void
  gxv_mort_subtable_type1_substTable_validate( FT_Bytes       table,
                                               FT_Bytes       limit,
                                               GXV_Validator  valid )
  {
    FT_Bytes   p = table;
    FT_UShort  num_gids = (FT_UShort)(
                 ((GXV_mort_subtable_type1_StateOptRec *)
                  (valid->statetable.optdata))->substitutionTable_length / 2 );
    FT_UShort  i;

    GXV_NAME_ENTER( "validating contents of substitutionTable" );
    for ( i = 0; i < num_gids ; i ++ )
    {
      FT_UShort  dst_gid;

      GXV_LIMIT_CHECK( 2 );
      dst_gid = FT_NEXT_USHORT( p );

      if ( dst_gid >= 0xFFFFU )
        continue;

      if ( dst_gid > valid->face->num_glyphs )
      {
        GXV_TRACE(( "substTable include too large gid[%d]=%d >"
                    " max defined gid #%d\n",
                    i, dst_gid, valid->face->num_glyphs ));
        if ( valid->root->level >= FT_VALIDATE_PARANOID )
          FT_INVALID_GLYPH_ID;
      }
    }

    GXV_EXIT;
  }

  /*
   * subtable for Contextual glyph substitution is a modified StateTable.
   * In addition to classTable, stateArray, and entryTable, the field
   * `substitutionTable' is added.
   */
  FT_LOCAL_DEF( void )
  gxv_mort_subtable_type1_validate( FT_Bytes       table,
                                    FT_Bytes       limit,
                                    GXV_Validator  valid )
  {
    FT_Bytes  p = table;

    GXV_mort_subtable_type1_StateOptRec  st_rec;

    GXV_NAME_ENTER( "mort chain subtable type1 (Contextual Glyph Subst)" );

    GXV_LIMIT_CHECK( GXV_MORT_SUBTABLE_TYPE1_HEADER_SIZE );

    valid->statetable.optdata =
      &st_rec;
    valid->statetable.optdata_load_func =
      gxv_mort_subtable_type1_substitutionTable_load;
    valid->statetable.subtable_setup_func =
      gxv_mort_subtable_type1_subtable_setup;
    valid->statetable.entry_glyphoffset_fmt =
      GXV_GLYPHOFFSET_ULONG;
    valid->statetable.entry_validate_func =

      gxv_mort_subtable_type1_entry_validate;
    gxv_StateTable_validate( p, limit, valid );

    gxv_mort_subtable_type1_substTable_validate(
      table + st_rec.substitutionTable,
      table + st_rec.substitutionTable + st_rec.substitutionTable_length,
      valid );

    GXV_EXIT;
  }

/* END */


code:
/***************************************************************************/
/*                                                                         */
/*  gxvmort2.c                                                             */
/*                                                                         */
/*    TrueTypeGX/AAT mort table validation                                 */
/*    body for type2 (Ligature Substitution) subtable.                     */
/*                                                                         */
/*  Copyright 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K.,       */
/*  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.                                        */
/*                                                                         */
/***************************************************************************/

/***************************************************************************/
/*                                                                         */
/* gxvalid is derived from both gxlayout module and otvalid module.        */
/* Development of gxlayout is supported by the Information-technology      */
/* Promotion Agency(IPA), Japan.                                           */
/*                                                                         */
/***************************************************************************/

#include "gxvmort.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_gxvmort

  typedef struct  GXV_mort_subtable_type2_StateOptRec_
  {
    FT_UShort  ligActionTable;
    FT_UShort  componentTable;
    FT_UShort  ligatureTable;
    FT_UShort  ligActionTable_length;
    FT_UShort  componentTable_length;
    FT_UShort  ligatureTable_length;

  }  GXV_mort_subtable_type2_StateOptRec,
    *GXV_mort_subtable_type2_StateOptRecData;

#define GXV_MORT_SUBTABLE_TYPE2_HEADER_SIZE \
          ( GXV_STATETABLE_HEADER_SIZE + 2 + 2 + 2 )

  static void
  gxv_mort_subtable_type2_opttable_load( FT_Bytes       table,
                                         FT_Bytes       limit,
                                         GXV_Validator  valid )
  {
    FT_Bytes p = table;
    GXV_mort_subtable_type2_StateOptRecData  optdata =
      (GXV_mort_subtable_type2_StateOptRecData)valid->statetable.optdata;

    GXV_LIMIT_CHECK( 2 + 2 + 2 );
    optdata->ligActionTable = FT_NEXT_USHORT( p );
    optdata->componentTable = FT_NEXT_USHORT( p );
    optdata->ligatureTable  = FT_NEXT_USHORT( p );

    GXV_TRACE(( "offset to ligActionTable=0x%04x\n",
                optdata->ligActionTable ));
    GXV_TRACE(( "offset to componentTable=0x%04x\n",
                optdata->componentTable ));
    GXV_TRACE(( "offset to ligatureTable=0x%04x\n",
                optdata->ligatureTable ));
  }

  static void
  gxv_mort_subtable_type2_subtable_setup( FT_UShort      table_size,
                                          FT_UShort      classTable,
                                          FT_UShort      stateArray,
                                          FT_UShort      entryTable,
                                          FT_UShort      *classTable_length_p,
                                          FT_UShort      *stateArray_length_p,
                                          FT_UShort      *entryTable_length_p,
                                          GXV_Validator  valid )
  {
    FT_UShort  o[6];
    FT_UShort  *l[6];
    FT_UShort  buff[7];

    GXV_mort_subtable_type2_StateOptRecData  optdata =
      (GXV_mort_subtable_type2_StateOptRecData)valid->statetable.optdata;

    GXV_NAME_ENTER( "subtable boundaries setup" );

    o[0] = classTable;
    o[1] = stateArray;
    o[2] = entryTable;
    o[3] = optdata->ligActionTable;
    o[4] = optdata->componentTable;
    o[5] = optdata->ligatureTable;
    l[0] = classTable_length_p;
    l[1] = stateArray_length_p;
    l[2] = entryTable_length_p;
    l[3] = &(optdata->ligActionTable_length);
    l[4] = &(optdata->componentTable_length);
    l[5] = &(optdata->ligatureTable_length);

    gxv_set_length_by_ushort_offset( o, l, buff, 6, table_size, valid );

    GXV_TRACE(( "classTable: offset=0x%04x length=0x%04x\n",
                classTable, *classTable_length_p ));
    GXV_TRACE(( "stateArray: offset=0x%04x length=0x%04x\n",
                stateArray, *stateArray_length_p ));
    GXV_TRACE(( "entryTable: offset=0x%04x length=0x%04x\n",
                entryTable, *entryTable_length_p ));
    GXV_TRACE(( "ligActionTable: offset=0x%04x length=0x%04x\n",
                optdata->ligActionTable,
                optdata->ligActionTable_length ));
    GXV_TRACE(( "componentTable: offset=0x%04x length=0x%04x\n",
                optdata->componentTable,
                optdata->componentTable_length ));
    GXV_TRACE(( "ligatureTable:  offset=0x%04x length=0x%04x\n",
                optdata->ligatureTable,
                optdata->ligatureTable_length ));

    GXV_EXIT;
  }

  static void
  gxv_mort_subtable_type2_ligActionOffset_validate(
    FT_Bytes       table,
    FT_UShort      ligActionOffset,
    GXV_Validator  valid )
  {
    /* access ligActionTable */
    GXV_mort_subtable_type2_StateOptRecData  optdata =
      (GXV_mort_subtable_type2_StateOptRecData)valid->statetable.optdata;

    FT_Bytes lat_base  = table + optdata->ligActionTable;
    FT_Bytes p         = table + ligActionOffset;
    FT_Bytes lat_limit = lat_base + optdata->ligActionTable;

    GXV_32BIT_ALIGNMENT_VALIDATE( ligActionOffset );
    if ( p < lat_base )
    {
      GXV_TRACE(( "too short offset 0x%04x: p < lat_base (%d byte rewind)\n",
                  ligActionOffset, lat_base - p ));

      /* FontValidator, ftxvalidator, ftxdumperfuser warn but continue */
      if ( valid->root->level >= FT_VALIDATE_PARANOID )
        FT_INVALID_OFFSET;
    }
    else if ( lat_limit < p )
    {
      GXV_TRACE(( "too large offset 0x%04x: lat_limit < p (%d byte overrun)\n",
                  ligActionOffset, p - lat_limit ));

      /* FontValidator, ftxvalidator, ftxdumperfuser warn but continue */
      if ( valid->root->level >= FT_VALIDATE_PARANOID )
        FT_INVALID_OFFSET;
    }
    else
    {
      /* validate entry in ligActionTable */
      FT_ULong   lig_action;
      FT_UShort  last;
      FT_UShort  store;
      FT_ULong   offset;

      lig_action = FT_NEXT_ULONG( p );
      last   = (FT_UShort)( ( lig_action >> 31 ) & 1 );
      store  = (FT_UShort)( ( lig_action >> 30 ) & 1 );

      offset = lig_action & 0x3FFFFFFFUL;
    }
  }

  static void
  gxv_mort_subtable_type2_entry_validate(
    FT_Byte                         state,
    FT_UShort                       flags,
    GXV_StateTable_GlyphOffsetCPtr  glyphOffset_p,
    FT_Bytes                        table,
    FT_Bytes                        limit,
    GXV_Validator                   valid )
  {
    FT_UShort setComponent;
    FT_UShort dontAdvance;
    FT_UShort offset;

    FT_UNUSED( state );
    FT_UNUSED( glyphOffset_p );
    FT_UNUSED( limit );

    setComponent = (FT_UShort)( ( flags >> 15 ) & 1 );
    dontAdvance  = (FT_UShort)( ( flags >> 14 ) & 1 );

    offset = (FT_UShort)( flags & 0x3FFFU );

    if ( 0 < offset )
      gxv_mort_subtable_type2_ligActionOffset_validate( table, offset,
                                                        valid );
  }

  static void
  gxv_mort_subtable_type2_ligatureTable_validate( FT_Bytes       table,
                                                  GXV_Validator  valid )
  {
    GXV_mort_subtable_type2_StateOptRecData  optdata =
      (GXV_mort_subtable_type2_StateOptRecData)valid->statetable.optdata;

    FT_Bytes p     = table + optdata->ligatureTable;
    FT_Bytes limit = table + optdata->ligatureTable
                           + optdata->ligatureTable_length;

    GXV_NAME_ENTER( "mort chain subtable type2 - substitutionTable" );
    if ( 0 != optdata->ligatureTable )
    {
      /* Apple does not give specification of ligatureTable format */
      while ( p < limit )
      {
        FT_UShort  lig_gid;

        GXV_LIMIT_CHECK( 2 );
        lig_gid = FT_NEXT_USHORT( p );
      }
    }
    GXV_EXIT;
  }

  FT_LOCAL_DEF( void )
  gxv_mort_subtable_type2_validate( FT_Bytes       table,
                                    FT_Bytes       limit,
                                    GXV_Validator  valid )
  {
    FT_Bytes  p = table;

    GXV_mort_subtable_type2_StateOptRec  lig_rec;

    GXV_NAME_ENTER( "mort chain subtable type2 (Ligature Substitution)" );

    GXV_LIMIT_CHECK( GXV_MORT_SUBTABLE_TYPE2_HEADER_SIZE );

    valid->statetable.optdata =
      &lig_rec;
    valid->statetable.optdata_load_func =
      gxv_mort_subtable_type2_opttable_load;
    valid->statetable.subtable_setup_func =
      gxv_mort_subtable_type2_subtable_setup;
    valid->statetable.entry_glyphoffset_fmt =
      GXV_GLYPHOFFSET_NONE;
    valid->statetable.entry_validate_func =
      gxv_mort_subtable_type2_entry_validate;

    gxv_StateTable_validate( p, limit, valid );

    p += valid->subtable_length;
    gxv_mort_subtable_type2_ligatureTable_validate( table, valid );

    valid->subtable_length = p - table;

    GXV_EXIT;
  }

/* END */


code:
/***************************************************************************/
/*                                                                         */
/*  gxvmort4.c                                                             */
/*                                                                         */
/*    TrueTypeGX/AAT mort table validation                                 */
/*    body for type4 (Non-Contextual Glyph Substitution) subtable.         */
/*                                                                         */
/*  Copyright 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K.,       */
/*  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.                                        */
/*                                                                         */
/***************************************************************************/

/***************************************************************************/
/*                                                                         */
/* gxvalid is derived from both gxlayout module and otvalid module.        */
/* Development of gxlayout is supported by the Information-technology      */
/* Promotion Agency(IPA), Japan.                                           */
/*                                                                         */
/***************************************************************************/

#include "gxvmort.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_gxvmort

  static void
  gxv_mort_subtable_type4_lookupval_validate( FT_UShort            glyph,
                                              GXV_LookupValueCPtr  value_p,
                                              GXV_Validator        valid )
  {
    FT_UNUSED( glyph );

    gxv_glyphid_validate( value_p->u, valid );
  }

  /*
    +===============+ --------+
    | lookup header |         |
    +===============+         |
    | BinSrchHeader |         |
    +===============+         |
    | lastGlyph[0]  |         |
    +---------------+         |
    | firstGlyph[0] |         |    head of lookup table
    +---------------+         |             +
    | offset[0]     |    ->   |          offset            [byte]
    +===============+         |             +
    | lastGlyph[1]  |         | (glyphID - firstGlyph) * 2 [byte]
    +---------------+         |
    | firstGlyph[1] |         |
    +---------------+         |
    | offset[1]     |         |
    +===============+         |
                              |
     ....                     |
                              |
    16bit value array         |
    +===============+         |
    |     value     | <-------+
     ....
  */

  static GXV_LookupValueDesc
  gxv_mort_subtable_type4_lookupfmt4_transit(
    FT_UShort            relative_gindex,
    GXV_LookupValueCPtr  base_value_p,
    FT_Bytes             lookuptbl_limit,
    GXV_Validator        valid )
  {
    FT_Bytes             p;
    FT_Bytes             limit;
    FT_UShort            offset;
    GXV_LookupValueDesc  value;

    /* XXX: check range? */
    offset = (FT_UShort)( base_value_p->u +
                          relative_gindex * sizeof ( FT_UShort ) );

    p     = valid->lookuptbl_head + offset;
    limit = lookuptbl_limit;

    GXV_LIMIT_CHECK( 2 );
    value.u = FT_NEXT_USHORT( p );

    return value;
  }

  FT_LOCAL_DEF( void )
  gxv_mort_subtable_type4_validate( FT_Bytes       table,
                                    FT_Bytes       limit,
                                    GXV_Validator  valid )
  {
    FT_Bytes  p = table;

    GXV_NAME_ENTER( "mort chain subtable type4 "
                    "(Non-Contextual Glyph Substitution)" );

    valid->lookupval_sign   = GXV_LOOKUPVALUE_UNSIGNED;
    valid->lookupval_func   = gxv_mort_subtable_type4_lookupval_validate;
    valid->lookupfmt4_trans = gxv_mort_subtable_type4_lookupfmt4_transit;

    gxv_LookupTable_validate( p, limit, valid );

    GXV_EXIT;
  }

/* END */


code:
/***************************************************************************/
/*                                                                         */
/*  gxvmort5.c                                                             */
/*                                                                         */
/*    TrueTypeGX/AAT mort table validation                                 */
/*    body for type5 (Contextual Glyph Insertion) subtable.                */
/*                                                                         */
/*  Copyright 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K.,       */
/*  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.                                        */
/*                                                                         */
/***************************************************************************/

/***************************************************************************/
/*                                                                         */
/* gxvalid is derived from both gxlayout module and otvalid module.        */
/* Development of gxlayout is supported by the Information-technology      */
/* Promotion Agency(IPA), Japan.                                           */
/*                                                                         */
/***************************************************************************/

#include "gxvmort.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_gxvmort

  /*
   * mort subtable type5 (Contextual Glyph Insertion)
   * has the format of StateTable with insertion-glyph-list,
   * but without name.  The offset is given by glyphOffset in
   * entryTable.  There is no table location declaration
   * like xxxTable.
   */

  typedef struct  GXV_mort_subtable_type5_StateOptRec_
  {
    FT_UShort   classTable;
    FT_UShort   stateArray;
    FT_UShort   entryTable;

#define GXV_MORT_SUBTABLE_TYPE5_HEADER_SIZE  GXV_STATETABLE_HEADER_SIZE

    FT_UShort*  classTable_length_p;
    FT_UShort*  stateArray_length_p;
    FT_UShort*  entryTable_length_p;

  }  GXV_mort_subtable_type5_StateOptRec,
    *GXV_mort_subtable_type5_StateOptRecData;

  FT_LOCAL_DEF( void )
  gxv_mort_subtable_type5_subtable_setup( FT_UShort      table_size,
                                          FT_UShort      classTable,
                                          FT_UShort      stateArray,
                                          FT_UShort      entryTable,
                                          FT_UShort*     classTable_length_p,
                                          FT_UShort*     stateArray_length_p,
                                          FT_UShort*     entryTable_length_p,
                                          GXV_Validator  valid )
  {
    GXV_mort_subtable_type5_StateOptRecData  optdata =
      (GXV_mort_subtable_type5_StateOptRecData)valid->statetable.optdata;

    gxv_StateTable_subtable_setup( table_size,
                                   classTable,
                                   stateArray,
                                   entryTable,
                                   classTable_length_p,
                                   stateArray_length_p,
                                   entryTable_length_p,
                                   valid );

    optdata->classTable = classTable;
    optdata->stateArray = stateArray;
    optdata->entryTable = entryTable;

    optdata->classTable_length_p = classTable_length_p;
    optdata->stateArray_length_p = stateArray_length_p;
    optdata->entryTable_length_p = entryTable_length_p;
  }

  static void
  gxv_mort_subtable_type5_InsertList_validate( FT_UShort      offset,
                                               FT_UShort      count,
                                               FT_Bytes       table,
                                               FT_Bytes       limit,
                                               GXV_Validator  valid )
  {
    /*
     * We don't know the range of insertion-glyph-list.
     * Set range by whole of state table.
     */
    FT_Bytes  p = table + offset;

    GXV_mort_subtable_type5_StateOptRecData  optdata =
      (GXV_mort_subtable_type5_StateOptRecData)valid->statetable.optdata;

    if ( optdata->classTable < offset                                   &&
         offset < optdata->classTable + *(optdata->classTable_length_p) )
      GXV_TRACE(( " offset runs into ClassTable" ));
    if ( optdata->stateArray < offset                                   &&
         offset < optdata->stateArray + *(optdata->stateArray_length_p) )
      GXV_TRACE(( " offset runs into StateArray" ));
    if ( optdata->entryTable < offset                                   &&
         offset < optdata->entryTable + *(optdata->entryTable_length_p) )
      GXV_TRACE(( " offset runs into EntryTable" ));

    while ( p < table + offset + ( count * 2 ) )
    {
      FT_UShort insert_glyphID;

      GXV_LIMIT_CHECK( 2 );
      insert_glyphID = FT_NEXT_USHORT( p );
      GXV_TRACE(( " 0x%04x", insert_glyphID ));
    }

    GXV_TRACE(( "\n" ));
  }

  static void
  gxv_mort_subtable_type5_entry_validate(
    FT_Byte                         state,
    FT_UShort                       flags,
    GXV_StateTable_GlyphOffsetCPtr  glyphOffset,
    FT_Bytes                        table,
    FT_Bytes                        limit,
    GXV_Validator                   valid )
  {
    FT_Bool    setMark;
    FT_Bool    dontAdvance;
    FT_Bool    currentIsKashidaLike;
    FT_Bool    markedIsKashidaLike;
    FT_Bool    currentInsertBefore;
    FT_Bool    markedInsertBefore;
    FT_Byte    currentInsertCount;
    FT_Byte    markedInsertCount;
    FT_UShort  currentInsertList;
    FT_UShort  markedInsertList;

    FT_UNUSED( state );

    setMark              = FT_BOOL( ( flags >> 15 ) & 1 );
    dontAdvance          = FT_BOOL( ( flags >> 14 ) & 1 );
    currentIsKashidaLike = FT_BOOL( ( flags >> 13 ) & 1 );
    markedIsKashidaLike  = FT_BOOL( ( flags >> 12 ) & 1 );
    currentInsertBefore  = FT_BOOL( ( flags >> 11 ) & 1 );
    markedInsertBefore   = FT_BOOL( ( flags >> 10 ) & 1 );

    currentInsertCount   = (FT_Byte)( ( flags >> 5 ) & 0x1F   );
    markedInsertCount    = (FT_Byte)(   flags        & 0x001F );

    currentInsertList    = (FT_UShort)( glyphOffset->ul >> 16 );
    markedInsertList     = (FT_UShort)( glyphOffset->ul       );

    if ( 0 != currentInsertList && 0 != currentInsertCount )
    {
      gxv_mort_subtable_type5_InsertList_validate( currentInsertList,
                                                   currentInsertCount,
                                                   table,
                                                   limit,
                                                   valid );
    }

    if ( 0 != markedInsertList && 0 != markedInsertCount )
    {
      gxv_mort_subtable_type5_InsertList_validate( markedInsertList,
                                                   markedInsertCount,
                                                   table,
                                                   limit,
                                                   valid );
    }
  }

  FT_LOCAL_DEF( void )
  gxv_mort_subtable_type5_validate( FT_Bytes       table,
                                    FT_Bytes       limit,
                                    GXV_Validator  valid )
  {
    FT_Bytes  p = table;

    GXV_mort_subtable_type5_StateOptRec      et_rec;
    GXV_mort_subtable_type5_StateOptRecData  et = &et_rec;

    GXV_NAME_ENTER( "mort chain subtable type5 (Glyph Insertion)" );

    GXV_LIMIT_CHECK( GXV_MORT_SUBTABLE_TYPE5_HEADER_SIZE );

    valid->statetable.optdata =
      et;
    valid->statetable.optdata_load_func =
      NULL;
    valid->statetable.subtable_setup_func =
      gxv_mort_subtable_type5_subtable_setup;
    valid->statetable.entry_glyphoffset_fmt =
      GXV_GLYPHOFFSET_ULONG;
    valid->statetable.entry_validate_func =
      gxv_mort_subtable_type5_entry_validate;

    gxv_StateTable_validate( p, limit, valid );

    GXV_EXIT;
  }

/* END */