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 */
722