tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

gxvjust.c (20583B)


      1 /****************************************************************************
      2 *
      3 * gxvjust.c
      4 *
      5 *   TrueTypeGX/AAT just table validation (body).
      6 *
      7 * Copyright (C) 2005-2025 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 */