(root)/
freetype-2.13.2/
src/
gxvalid/
gxvjust.c
       1  /****************************************************************************
       2   *
       3   * gxvjust.c
       4   *
       5   *   TrueTypeGX/AAT just table validation (body).
       6   *
       7   * Copyright (C) 2005-2023 by
       8   * suzuki toshiya, Masatake YAMATO, Red Hat K.K.,
       9   * David Turner, Robert Wilhelm, and Werner Lemberg.
      10   *
      11   * This file is part of the FreeType project, and may only be used,
      12   * modified, and distributed under the terms of the FreeType project
      13   * license, LICENSE.TXT.  By continuing to use, modify, or distribute
      14   * this file you indicate that you have read the license and
      15   * understand and accept it fully.
      16   *
      17   */
      18  
      19  /****************************************************************************
      20   *
      21   * gxvalid is derived from both gxlayout module and otvalid module.
      22   * Development of gxlayout is supported by the Information-technology
      23   * Promotion Agency(IPA), Japan.
      24   *
      25   */
      26  
      27  
      28  #include "gxvalid.h"
      29  #include "gxvcommn.h"
      30  
      31  #include <freetype/ftsnames.h>
      32  
      33  
      34    /**************************************************************************
      35     *
      36     * The macro FT_COMPONENT is used in trace mode.  It is an implicit
      37     * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
      38     * messages during execution.
      39     */
      40  #undef  FT_COMPONENT
      41  #define FT_COMPONENT  gxvjust
      42  
      43    /*
      44     * referred `just' table format specification:
      45     * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6just.html
      46     * last updated 2000.
      47     * ----------------------------------------------
      48     * [JUST HEADER]: GXV_JUST_HEADER_SIZE
      49     * version     (fixed:  32bit) = 0x00010000
      50     * format      (uint16: 16bit) = 0 is only defined (2000)
      51     * horizOffset (uint16: 16bit)
      52     * vertOffset  (uint16: 16bit)
      53     * ----------------------------------------------
      54     */
      55  
      56    typedef struct  GXV_just_DataRec_
      57    {
      58      FT_UShort  wdc_offset_max;
      59      FT_UShort  wdc_offset_min;
      60      FT_UShort  pc_offset_max;
      61      FT_UShort  pc_offset_min;
      62  
      63    } GXV_just_DataRec, *GXV_just_Data;
      64  
      65  
      66  #define  GXV_JUST_DATA( a )  GXV_TABLE_DATA( just, a )
      67  
      68  
      69    /* GX just table does not define their subset of GID */
      70    static void
      71    gxv_just_check_max_gid( FT_UShort         gid,
      72                            const FT_String*  msg_tag,
      73                            GXV_Validator     gxvalid )
      74    {
      75      FT_UNUSED( msg_tag );
      76  
      77      if ( gid < gxvalid->face->num_glyphs )
      78        return;
      79  
      80      GXV_TRACE(( "just table includes too large %s"
      81                  " GID=%d > %ld (in maxp)\n",
      82                  msg_tag, gid, gxvalid->face->num_glyphs ));
      83      GXV_SET_ERR_IF_PARANOID( FT_INVALID_GLYPH_ID );
      84    }
      85  
      86  
      87    static void
      88    gxv_just_wdp_entry_validate( FT_Bytes       table,
      89                                 FT_Bytes       limit,
      90                                 GXV_Validator  gxvalid )
      91    {
      92      FT_Bytes   p = table;
      93      FT_ULong   justClass;
      94  #ifdef GXV_LOAD_UNUSED_VARS
      95      FT_Fixed   beforeGrowLimit;
      96      FT_Fixed   beforeShrinkGrowLimit;
      97      FT_Fixed   afterGrowLimit;
      98      FT_Fixed   afterShrinkGrowLimit;
      99      FT_UShort  growFlags;
     100      FT_UShort  shrinkFlags;
     101  #endif
     102  
     103  
     104      GXV_LIMIT_CHECK( 4 + 4 + 4 + 4 + 4 + 2 + 2 );
     105      justClass             = FT_NEXT_ULONG( p );
     106  #ifndef GXV_LOAD_UNUSED_VARS
     107      p += 4 + 4 + 4 + 4 + 2 + 2;
     108  #else
     109      beforeGrowLimit       = FT_NEXT_ULONG( p );
     110      beforeShrinkGrowLimit = FT_NEXT_ULONG( p );
     111      afterGrowLimit        = FT_NEXT_ULONG( p );
     112      afterShrinkGrowLimit  = FT_NEXT_ULONG( p );
     113      growFlags             = FT_NEXT_USHORT( p );
     114      shrinkFlags           = FT_NEXT_USHORT( p );
     115  #endif
     116  
     117      /* According to Apple spec, only 7bits in justClass is used */
     118      if ( ( justClass & 0xFFFFFF80UL ) != 0 )
     119      {
     120        GXV_TRACE(( "just table includes non-zero value"
     121                    " in unused justClass higher bits"
     122                    " of WidthDeltaPair" ));
     123        GXV_SET_ERR_IF_PARANOID( FT_INVALID_DATA );
     124      }
     125  
     126      gxvalid->subtable_length = (FT_ULong)( p - table );
     127    }
     128  
     129  
     130    static void
     131    gxv_just_wdc_entry_validate( FT_Bytes       table,
     132                                 FT_Bytes       limit,
     133                                 GXV_Validator  gxvalid )
     134    {
     135      FT_Bytes  p = table;
     136      FT_ULong  count, i;
     137  
     138  
     139      GXV_LIMIT_CHECK( 4 );
     140      count = FT_NEXT_ULONG( p );
     141      for ( i = 0; i < count; i++ )
     142      {
     143        GXV_TRACE(( "validating wdc pair %lu/%lu\n", i + 1, count ));
     144        gxv_just_wdp_entry_validate( p, limit, gxvalid );
     145        p += gxvalid->subtable_length;
     146      }
     147  
     148      gxvalid->subtable_length = (FT_ULong)( p - table );
     149    }
     150  
     151  
     152    static void
     153    gxv_just_widthDeltaClusters_validate( FT_Bytes       table,
     154                                          FT_Bytes       limit,
     155                                          GXV_Validator  gxvalid )
     156    {
     157      FT_Bytes  p         = table;
     158      FT_Bytes  wdc_end   = table + GXV_JUST_DATA( wdc_offset_max );
     159  
     160  
     161      GXV_NAME_ENTER( "just justDeltaClusters" );
     162  
     163      if ( limit <= wdc_end )
     164        FT_INVALID_OFFSET;
     165  
     166      while ( p <= wdc_end )
     167      {
     168        gxv_just_wdc_entry_validate( p, limit, gxvalid );
     169        p += gxvalid->subtable_length;
     170      }
     171  
     172      gxvalid->subtable_length = (FT_ULong)( p - table );
     173  
     174      GXV_EXIT;
     175    }
     176  
     177  
     178    static void
     179    gxv_just_actSubrecord_type0_validate( FT_Bytes       table,
     180                                          FT_Bytes       limit,
     181                                          GXV_Validator  gxvalid )
     182    {
     183      FT_Bytes   p = table;
     184  
     185      FT_Fixed   lowerLimit;
     186      FT_Fixed   upperLimit;
     187  #ifdef GXV_LOAD_UNUSED_VARS
     188      FT_UShort  order;
     189  #endif
     190      FT_UShort  decomposedCount;
     191  
     192      FT_UInt    i;
     193  
     194  
     195      GXV_LIMIT_CHECK( 4 + 4 + 2 + 2 );
     196      lowerLimit      = FT_NEXT_LONG( p );
     197      upperLimit      = FT_NEXT_LONG( p );
     198  #ifdef GXV_LOAD_UNUSED_VARS
     199      order           = FT_NEXT_USHORT( p );
     200  #else
     201      p += 2;
     202  #endif
     203      decomposedCount = FT_NEXT_USHORT( p );
     204  
     205      if ( lowerLimit >= upperLimit )
     206      {
     207        GXV_TRACE(( "just table includes invalid range spec:"
     208                    " lowerLimit(%ld) > upperLimit(%ld)\n",
     209                    lowerLimit, upperLimit ));
     210        GXV_SET_ERR_IF_PARANOID( FT_INVALID_DATA );
     211      }
     212  
     213      for ( i = 0; i < decomposedCount; i++ )
     214      {
     215        FT_UShort glyphs;
     216  
     217  
     218        GXV_LIMIT_CHECK( 2 );
     219        glyphs = FT_NEXT_USHORT( p );
     220        gxv_just_check_max_gid( glyphs, "type0:glyphs", gxvalid );
     221      }
     222  
     223      gxvalid->subtable_length = (FT_ULong)( p - table );
     224    }
     225  
     226  
     227    static void
     228    gxv_just_actSubrecord_type1_validate( FT_Bytes       table,
     229                                          FT_Bytes       limit,
     230                                          GXV_Validator  gxvalid )
     231    {
     232      FT_Bytes   p = table;
     233      FT_UShort  addGlyph;
     234  
     235  
     236      GXV_LIMIT_CHECK( 2 );
     237      addGlyph = FT_NEXT_USHORT( p );
     238  
     239      gxv_just_check_max_gid( addGlyph, "type1:addGlyph", gxvalid );
     240  
     241      gxvalid->subtable_length = (FT_ULong)( p - table );
     242    }
     243  
     244  
     245    static void
     246    gxv_just_actSubrecord_type2_validate( FT_Bytes       table,
     247                                          FT_Bytes       limit,
     248                                          GXV_Validator  gxvalid )
     249    {
     250      FT_Bytes   p = table;
     251  #ifdef GXV_LOAD_UNUSED_VARS
     252      FT_Fixed      substThreshhold; /* Apple misspelled "Threshhold" */
     253  #endif
     254      FT_UShort  addGlyph;
     255      FT_UShort  substGlyph;
     256  
     257  
     258      GXV_LIMIT_CHECK( 4 + 2 + 2 );
     259  #ifdef GXV_LOAD_UNUSED_VARS
     260      substThreshhold = FT_NEXT_ULONG( p );
     261  #else
     262      p += 4;
     263  #endif
     264      addGlyph        = FT_NEXT_USHORT( p );
     265      substGlyph      = FT_NEXT_USHORT( p );
     266  
     267      if ( addGlyph != 0xFFFF )
     268        gxv_just_check_max_gid( addGlyph, "type2:addGlyph", gxvalid );
     269  
     270      gxv_just_check_max_gid( substGlyph, "type2:substGlyph", gxvalid );
     271  
     272      gxvalid->subtable_length = (FT_ULong)( p - table );
     273    }
     274  
     275  
     276    static void
     277    gxv_just_actSubrecord_type4_validate( FT_Bytes       table,
     278                                          FT_Bytes       limit,
     279                                          GXV_Validator  gxvalid )
     280    {
     281      FT_Bytes  p = table;
     282      FT_ULong  variantsAxis;
     283      FT_Fixed  minimumLimit;
     284      FT_Fixed  noStretchValue;
     285      FT_Fixed  maximumLimit;
     286  
     287  
     288      GXV_LIMIT_CHECK( 4 + 4 + 4 + 4 );
     289      variantsAxis   = FT_NEXT_ULONG( p );
     290      minimumLimit   = FT_NEXT_LONG( p );
     291      noStretchValue = FT_NEXT_LONG( p );
     292      maximumLimit   = FT_NEXT_LONG( p );
     293  
     294      gxvalid->subtable_length = (FT_ULong)( p - table );
     295  
     296      if ( variantsAxis != 0x64756374L ) /* 'duct' */
     297        GXV_TRACE(( "variantsAxis 0x%08lx is non default value",
     298                     variantsAxis ));
     299  
     300      if ( minimumLimit > noStretchValue )
     301        GXV_TRACE(( "type4:minimumLimit 0x%08lx > noStretchValue 0x%08lx\n",
     302                    minimumLimit, noStretchValue ));
     303      else if ( noStretchValue > maximumLimit )
     304        GXV_TRACE(( "type4:noStretchValue 0x%08lx > maximumLimit 0x%08lx\n",
     305                    noStretchValue, maximumLimit ));
     306      else if ( !IS_PARANOID_VALIDATION )
     307        return;
     308  
     309      FT_INVALID_DATA;
     310    }
     311  
     312  
     313    static void
     314    gxv_just_actSubrecord_type5_validate( FT_Bytes       table,
     315                                          FT_Bytes       limit,
     316                                          GXV_Validator  gxvalid )
     317    {
     318      FT_Bytes   p = table;
     319      FT_UShort  flags;
     320      FT_UShort  glyph;
     321  
     322  
     323      GXV_LIMIT_CHECK( 2 + 2 );
     324      flags = FT_NEXT_USHORT( p );
     325      glyph = FT_NEXT_USHORT( p );
     326  
     327      if ( flags )
     328        GXV_TRACE(( "type5: nonzero value 0x%04x in unused flags\n",
     329                     flags ));
     330      gxv_just_check_max_gid( glyph, "type5:glyph", gxvalid );
     331  
     332      gxvalid->subtable_length = (FT_ULong)( p - table );
     333    }
     334  
     335  
     336    /* parse single actSubrecord */
     337    static void
     338    gxv_just_actSubrecord_validate( FT_Bytes       table,
     339                                    FT_Bytes       limit,
     340                                    GXV_Validator  gxvalid )
     341    {
     342      FT_Bytes   p = table;
     343      FT_UShort  actionClass;
     344      FT_UShort  actionType;
     345      FT_ULong   actionLength;
     346  
     347  
     348      GXV_NAME_ENTER( "just actSubrecord" );
     349  
     350      GXV_LIMIT_CHECK( 2 + 2 + 4 );
     351      actionClass  = FT_NEXT_USHORT( p );
     352      actionType   = FT_NEXT_USHORT( p );
     353      actionLength = FT_NEXT_ULONG( p );
     354  
     355      /* actionClass is related with justClass using 7bit only */
     356      if ( ( actionClass & 0xFF80 ) != 0 )
     357        GXV_SET_ERR_IF_PARANOID( FT_INVALID_DATA );
     358  
     359      if ( actionType == 0 )
     360        gxv_just_actSubrecord_type0_validate( p, limit, gxvalid );
     361      else if ( actionType == 1 )
     362        gxv_just_actSubrecord_type1_validate( p, limit, gxvalid );
     363      else if ( actionType == 2 )
     364        gxv_just_actSubrecord_type2_validate( p, limit, gxvalid );
     365      else if ( actionType == 3 )
     366        ;                         /* Stretch glyph action: no actionData */
     367      else if ( actionType == 4 )
     368        gxv_just_actSubrecord_type4_validate( p, limit, gxvalid );
     369      else if ( actionType == 5 )
     370        gxv_just_actSubrecord_type5_validate( p, limit, gxvalid );
     371      else
     372        FT_INVALID_DATA;
     373  
     374      gxvalid->subtable_length = actionLength;
     375  
     376      GXV_EXIT;
     377    }
     378  
     379  
     380    static void
     381    gxv_just_pcActionRecord_validate( FT_Bytes       table,
     382                                      FT_Bytes       limit,
     383                                      GXV_Validator  gxvalid )
     384    {
     385      FT_Bytes  p = table;
     386      FT_ULong  actionCount;
     387      FT_ULong  i;
     388  
     389  
     390      GXV_LIMIT_CHECK( 4 );
     391      actionCount = FT_NEXT_ULONG( p );
     392      GXV_TRACE(( "actionCount = %lu\n", actionCount ));
     393  
     394      for ( i = 0; i < actionCount; i++ )
     395      {
     396        gxv_just_actSubrecord_validate( p, limit, gxvalid );
     397        p += gxvalid->subtable_length;
     398      }
     399  
     400      gxvalid->subtable_length = (FT_ULong)( p - table );
     401  
     402      GXV_EXIT;
     403    }
     404  
     405  
     406    static void
     407    gxv_just_pcTable_LookupValue_entry_validate( FT_UShort            glyph,
     408                                                 GXV_LookupValueCPtr  value_p,
     409                                                 GXV_Validator        gxvalid )
     410    {
     411      FT_UNUSED( glyph );
     412  
     413      if ( value_p->u > GXV_JUST_DATA( pc_offset_max ) )
     414        GXV_JUST_DATA( pc_offset_max ) = value_p->u;
     415      if ( value_p->u < GXV_JUST_DATA( pc_offset_max ) )
     416        GXV_JUST_DATA( pc_offset_min ) = value_p->u;
     417    }
     418  
     419  
     420    static void
     421    gxv_just_pcLookupTable_validate( FT_Bytes       table,
     422                                     FT_Bytes       limit,
     423                                     GXV_Validator  gxvalid )
     424    {
     425      FT_Bytes  p = table;
     426  
     427  
     428      GXV_NAME_ENTER( "just pcLookupTable" );
     429      GXV_JUST_DATA( pc_offset_max ) = 0x0000;
     430      GXV_JUST_DATA( pc_offset_min ) = 0xFFFFU;
     431  
     432      gxvalid->lookupval_sign = GXV_LOOKUPVALUE_UNSIGNED;
     433      gxvalid->lookupval_func = gxv_just_pcTable_LookupValue_entry_validate;
     434  
     435      gxv_LookupTable_validate( p, limit, gxvalid );
     436  
     437      /* subtable_length is set by gxv_LookupTable_validate() */
     438  
     439      GXV_EXIT;
     440    }
     441  
     442  
     443    static void
     444    gxv_just_postcompTable_validate( FT_Bytes       table,
     445                                     FT_Bytes       limit,
     446                                     GXV_Validator  gxvalid )
     447    {
     448      FT_Bytes  p = table;
     449  
     450  
     451      GXV_NAME_ENTER( "just postcompTable" );
     452  
     453      gxv_just_pcLookupTable_validate( p, limit, gxvalid );
     454      p += gxvalid->subtable_length;
     455  
     456      gxv_just_pcActionRecord_validate( p, limit, gxvalid );
     457      p += gxvalid->subtable_length;
     458  
     459      gxvalid->subtable_length = (FT_ULong)( p - table );
     460  
     461      GXV_EXIT;
     462    }
     463  
     464  
     465    static void
     466    gxv_just_classTable_entry_validate(
     467      FT_Byte                         state,
     468      FT_UShort                       flags,
     469      GXV_StateTable_GlyphOffsetCPtr  glyphOffset_p,
     470      FT_Bytes                        table,
     471      FT_Bytes                        limit,
     472      GXV_Validator                   gxvalid )
     473    {
     474  #ifdef GXV_LOAD_UNUSED_VARS
     475      /* TODO: validate markClass & currentClass */
     476      FT_UShort  setMark;
     477      FT_UShort  dontAdvance;
     478      FT_UShort  markClass;
     479      FT_UShort  currentClass;
     480  #endif
     481  
     482      FT_UNUSED( state );
     483      FT_UNUSED( glyphOffset_p );
     484      FT_UNUSED( table );
     485      FT_UNUSED( limit );
     486      FT_UNUSED( gxvalid );
     487  
     488  #ifndef GXV_LOAD_UNUSED_VARS
     489      FT_UNUSED( flags );
     490  #else
     491      setMark      = (FT_UShort)( ( flags >> 15 ) & 1    );
     492      dontAdvance  = (FT_UShort)( ( flags >> 14 ) & 1    );
     493      markClass    = (FT_UShort)( ( flags >> 7  ) & 0x7F );
     494      currentClass = (FT_UShort)(   flags         & 0x7F );
     495  #endif
     496    }
     497  
     498  
     499    static void
     500    gxv_just_justClassTable_validate ( FT_Bytes       table,
     501                                       FT_Bytes       limit,
     502                                       GXV_Validator  gxvalid )
     503    {
     504      FT_Bytes   p = table;
     505      FT_UShort  length;
     506      FT_UShort  coverage;
     507      FT_ULong   subFeatureFlags;
     508  
     509  
     510      GXV_NAME_ENTER( "just justClassTable" );
     511  
     512      GXV_LIMIT_CHECK( 2 + 2 + 4 );
     513      length          = FT_NEXT_USHORT( p );
     514      coverage        = FT_NEXT_USHORT( p );
     515      subFeatureFlags = FT_NEXT_ULONG( p );
     516  
     517      GXV_TRACE(( "  justClassTable: coverage = 0x%04x ", coverage ));
     518      if ( ( coverage & 0x4000 ) == 0  )
     519        GXV_TRACE(( "ascending\n" ));
     520      else
     521        GXV_TRACE(( "descending\n" ));
     522  
     523      if ( subFeatureFlags )
     524        GXV_TRACE(( "  justClassTable: nonzero value (0x%08lx)"
     525                    " in unused subFeatureFlags\n", subFeatureFlags ));
     526  
     527      gxvalid->statetable.optdata               = NULL;
     528      gxvalid->statetable.optdata_load_func     = NULL;
     529      gxvalid->statetable.subtable_setup_func   = NULL;
     530      gxvalid->statetable.entry_glyphoffset_fmt = GXV_GLYPHOFFSET_NONE;
     531      gxvalid->statetable.entry_validate_func   =
     532        gxv_just_classTable_entry_validate;
     533  
     534      gxv_StateTable_validate( p, table + length, gxvalid );
     535  
     536      /* subtable_length is set by gxv_LookupTable_validate() */
     537  
     538      GXV_EXIT;
     539    }
     540  
     541  
     542    static void
     543    gxv_just_wdcTable_LookupValue_validate( FT_UShort            glyph,
     544                                            GXV_LookupValueCPtr  value_p,
     545                                            GXV_Validator        gxvalid )
     546    {
     547      FT_UNUSED( glyph );
     548  
     549      if ( value_p->u > GXV_JUST_DATA( wdc_offset_max ) )
     550        GXV_JUST_DATA( wdc_offset_max ) = value_p->u;
     551      if ( value_p->u < GXV_JUST_DATA( wdc_offset_min ) )
     552        GXV_JUST_DATA( wdc_offset_min ) = value_p->u;
     553    }
     554  
     555  
     556    static void
     557    gxv_just_justData_lookuptable_validate( FT_Bytes       table,
     558                                            FT_Bytes       limit,
     559                                            GXV_Validator  gxvalid )
     560    {
     561      FT_Bytes  p = table;
     562  
     563  
     564      GXV_JUST_DATA( wdc_offset_max ) = 0x0000;
     565      GXV_JUST_DATA( wdc_offset_min ) = 0xFFFFU;
     566  
     567      gxvalid->lookupval_sign = GXV_LOOKUPVALUE_UNSIGNED;
     568      gxvalid->lookupval_func = gxv_just_wdcTable_LookupValue_validate;
     569  
     570      gxv_LookupTable_validate( p, limit, gxvalid );
     571  
     572      /* subtable_length is set by gxv_LookupTable_validate() */
     573  
     574      GXV_EXIT;
     575    }
     576  
     577  
     578    /*
     579     * gxv_just_justData_validate() parses and validates horizData, vertData.
     580     */
     581    static void
     582    gxv_just_justData_validate( FT_Bytes       table,
     583                                FT_Bytes       limit,
     584                                GXV_Validator  gxvalid )
     585    {
     586      /*
     587       * following 3 offsets are measured from the start of `just'
     588       * (which table points to), not justData
     589       */
     590      FT_UShort  justClassTableOffset;
     591      FT_UShort  wdcTableOffset;
     592      FT_UShort  pcTableOffset;
     593      FT_Bytes   p = table;
     594  
     595      GXV_ODTECT( 4, odtect );
     596  
     597  
     598      GXV_NAME_ENTER( "just justData" );
     599  
     600      GXV_ODTECT_INIT( odtect );
     601      GXV_LIMIT_CHECK( 2 + 2 + 2 );
     602      justClassTableOffset = FT_NEXT_USHORT( p );
     603      wdcTableOffset       = FT_NEXT_USHORT( p );
     604      pcTableOffset        = FT_NEXT_USHORT( p );
     605  
     606      GXV_TRACE(( " (justClassTableOffset = 0x%04x)\n", justClassTableOffset ));
     607      GXV_TRACE(( " (wdcTableOffset = 0x%04x)\n", wdcTableOffset ));
     608      GXV_TRACE(( " (pcTableOffset = 0x%04x)\n", pcTableOffset ));
     609  
     610      gxv_just_justData_lookuptable_validate( p, limit, gxvalid );
     611      gxv_odtect_add_range( p, gxvalid->subtable_length,
     612                            "just_LookupTable", odtect );
     613  
     614      if ( wdcTableOffset )
     615      {
     616        gxv_just_widthDeltaClusters_validate(
     617          gxvalid->root->base + wdcTableOffset, limit, gxvalid );
     618        gxv_odtect_add_range( gxvalid->root->base + wdcTableOffset,
     619                              gxvalid->subtable_length, "just_wdcTable", odtect );
     620      }
     621  
     622      if ( pcTableOffset )
     623      {
     624        gxv_just_postcompTable_validate( gxvalid->root->base + pcTableOffset,
     625                                         limit, gxvalid );
     626        gxv_odtect_add_range( gxvalid->root->base + pcTableOffset,
     627                              gxvalid->subtable_length, "just_pcTable", odtect );
     628      }
     629  
     630      if ( justClassTableOffset )
     631      {
     632        gxv_just_justClassTable_validate(
     633          gxvalid->root->base + justClassTableOffset, limit, gxvalid );
     634        gxv_odtect_add_range( gxvalid->root->base + justClassTableOffset,
     635                              gxvalid->subtable_length, "just_justClassTable",
     636                              odtect );
     637      }
     638  
     639      gxv_odtect_validate( odtect, gxvalid );
     640  
     641      GXV_EXIT;
     642    }
     643  
     644  
     645    FT_LOCAL_DEF( void )
     646    gxv_just_validate( FT_Bytes      table,
     647                       FT_Face       face,
     648                       FT_Validator  ftvalid )
     649    {
     650      FT_Bytes           p     = table;
     651      FT_Bytes           limit = 0;
     652  
     653      GXV_ValidatorRec   gxvalidrec;
     654      GXV_Validator      gxvalid = &gxvalidrec;
     655      GXV_just_DataRec   justrec;
     656      GXV_just_Data      just = &justrec;
     657  
     658      FT_ULong           version;
     659      FT_UShort          format;
     660      FT_UShort          horizOffset;
     661      FT_UShort          vertOffset;
     662  
     663      GXV_ODTECT( 3, odtect );
     664  
     665  
     666      GXV_ODTECT_INIT( odtect );
     667  
     668      gxvalid->root       = ftvalid;
     669      gxvalid->table_data = just;
     670      gxvalid->face       = face;
     671  
     672      FT_TRACE3(( "validating `just' table\n" ));
     673      GXV_INIT;
     674  
     675      limit      = gxvalid->root->limit;
     676  
     677      GXV_LIMIT_CHECK( 4 + 2 + 2 + 2 );
     678      version     = FT_NEXT_ULONG( p );
     679      format      = FT_NEXT_USHORT( p );
     680      horizOffset = FT_NEXT_USHORT( p );
     681      vertOffset  = FT_NEXT_USHORT( p );
     682      gxv_odtect_add_range( table, (FT_ULong)( p - table ),
     683                            "just header", odtect );
     684  
     685  
     686      /* Version 1.0 (always:2000) */
     687      GXV_TRACE(( " (version = 0x%08lx)\n", version ));
     688      if ( version != 0x00010000UL )
     689        FT_INVALID_FORMAT;
     690  
     691      /* format 0 (always:2000) */
     692      GXV_TRACE(( " (format = 0x%04x)\n", format ));
     693      if ( format != 0x0000 )
     694          FT_INVALID_FORMAT;
     695  
     696      GXV_TRACE(( " (horizOffset = %d)\n", horizOffset  ));
     697      GXV_TRACE(( " (vertOffset = %d)\n", vertOffset  ));
     698  
     699  
     700      /* validate justData */
     701      if ( 0 < horizOffset )
     702      {
     703        gxv_just_justData_validate( table + horizOffset, limit, gxvalid );
     704        gxv_odtect_add_range( table + horizOffset, gxvalid->subtable_length,
     705                              "horizJustData", odtect );
     706      }
     707  
     708      if ( 0 < vertOffset )
     709      {
     710        gxv_just_justData_validate( table + vertOffset, limit, gxvalid );
     711        gxv_odtect_add_range( table + vertOffset, gxvalid->subtable_length,
     712                              "vertJustData", odtect );
     713      }
     714  
     715      gxv_odtect_validate( odtect, gxvalid );
     716  
     717      FT_TRACE4(( "\n" ));
     718    }
     719  
     720  
     721  /* END */