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