| 1 | /**************************************************************************** |
| 2 | * |
| 3 | * otvgsub.c |
| 4 | * |
| 5 | * OpenType GSUB table validation (body). |
| 6 | * |
| 7 | * Copyright (C) 2004-2023 by |
| 8 | * David Turner, Robert Wilhelm, and Werner Lemberg. |
| 9 | * |
| 10 | * This file is part of the FreeType project, and may only be used, |
| 11 | * modified, and distributed under the terms of the FreeType project |
| 12 | * license, LICENSE.TXT. By continuing to use, modify, or distribute |
| 13 | * this file you indicate that you have read the license and |
| 14 | * understand and accept it fully. |
| 15 | * |
| 16 | */ |
| 17 | |
| 18 | |
| 19 | #include "otvalid.h" |
| 20 | #include "otvcommn.h" |
| 21 | |
| 22 | |
| 23 | /************************************************************************** |
| 24 | * |
| 25 | * The macro FT_COMPONENT is used in trace mode. It is an implicit |
| 26 | * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log |
| 27 | * messages during execution. |
| 28 | */ |
| 29 | #undef FT_COMPONENT |
| 30 | #define FT_COMPONENT otvgsub |
| 31 | |
| 32 | |
| 33 | /*************************************************************************/ |
| 34 | /*************************************************************************/ |
| 35 | /***** *****/ |
| 36 | /***** GSUB LOOKUP TYPE 1 *****/ |
| 37 | /***** *****/ |
| 38 | /*************************************************************************/ |
| 39 | /*************************************************************************/ |
| 40 | |
| 41 | /* uses otvalid->glyph_count */ |
| 42 | |
| 43 | static void |
| 44 | otv_SingleSubst_validate( FT_Bytes table, |
| 45 | OTV_Validator otvalid ) |
| 46 | { |
| 47 | FT_Bytes p = table; |
| 48 | FT_UInt SubstFormat; |
| 49 | |
| 50 | |
| 51 | OTV_NAME_ENTER( "SingleSubst" ); |
| 52 | |
| 53 | OTV_LIMIT_CHECK( 2 ); |
| 54 | SubstFormat = FT_NEXT_USHORT( p ); |
| 55 | |
| 56 | OTV_TRACE(( " (format %d)\n" , SubstFormat )); |
| 57 | |
| 58 | switch ( SubstFormat ) |
| 59 | { |
| 60 | case 1: /* SingleSubstFormat1 */ |
| 61 | { |
| 62 | FT_Bytes Coverage; |
| 63 | FT_Int DeltaGlyphID; |
| 64 | FT_UInt first_cov, last_cov; |
| 65 | FT_UInt first_idx, last_idx; |
| 66 | |
| 67 | |
| 68 | OTV_LIMIT_CHECK( 4 ); |
| 69 | Coverage = table + FT_NEXT_USHORT( p ); |
| 70 | DeltaGlyphID = FT_NEXT_SHORT( p ); |
| 71 | |
| 72 | otv_Coverage_validate( Coverage, otvalid, -1 ); |
| 73 | |
| 74 | first_cov = otv_Coverage_get_first( Coverage ); |
| 75 | last_cov = otv_Coverage_get_last( Coverage ); |
| 76 | |
| 77 | /* These additions are modulo 65536. */ |
| 78 | first_idx = (FT_UInt)( (FT_Int)first_cov + DeltaGlyphID ) & 0xFFFFU; |
| 79 | last_idx = (FT_UInt)( (FT_Int)last_cov + DeltaGlyphID ) & 0xFFFFU; |
| 80 | |
| 81 | /* Since the maximum number of glyphs is 2^16 - 1 = 65535, */ |
| 82 | /* the largest possible glyph index is 65534. For this */ |
| 83 | /* reason there can't be a wrap-around region, which would */ |
| 84 | /* imply the use of the invalid glyph index 65535. */ |
| 85 | if ( first_idx > last_idx ) |
| 86 | FT_INVALID_DATA; |
| 87 | |
| 88 | if ( last_idx >= otvalid->glyph_count ) |
| 89 | FT_INVALID_DATA; |
| 90 | } |
| 91 | break; |
| 92 | |
| 93 | case 2: /* SingleSubstFormat2 */ |
| 94 | { |
| 95 | FT_UInt Coverage, GlyphCount; |
| 96 | |
| 97 | |
| 98 | OTV_LIMIT_CHECK( 4 ); |
| 99 | Coverage = FT_NEXT_USHORT( p ); |
| 100 | GlyphCount = FT_NEXT_USHORT( p ); |
| 101 | |
| 102 | OTV_TRACE(( " (GlyphCount = %d)\n" , GlyphCount )); |
| 103 | |
| 104 | otv_Coverage_validate( table + Coverage, |
| 105 | otvalid, |
| 106 | (FT_Int)GlyphCount ); |
| 107 | |
| 108 | OTV_LIMIT_CHECK( GlyphCount * 2 ); |
| 109 | |
| 110 | /* Substitute */ |
| 111 | for ( ; GlyphCount > 0; GlyphCount-- ) |
| 112 | if ( FT_NEXT_USHORT( p ) >= otvalid->glyph_count ) |
| 113 | FT_INVALID_GLYPH_ID; |
| 114 | } |
| 115 | break; |
| 116 | |
| 117 | default: |
| 118 | FT_INVALID_FORMAT; |
| 119 | } |
| 120 | |
| 121 | OTV_EXIT; |
| 122 | } |
| 123 | |
| 124 | |
| 125 | /*************************************************************************/ |
| 126 | /*************************************************************************/ |
| 127 | /***** *****/ |
| 128 | /***** GSUB LOOKUP TYPE 2 *****/ |
| 129 | /***** *****/ |
| 130 | /*************************************************************************/ |
| 131 | /*************************************************************************/ |
| 132 | |
| 133 | /* sets otvalid->extra1 (glyph count) */ |
| 134 | |
| 135 | static void |
| 136 | otv_MultipleSubst_validate( FT_Bytes table, |
| 137 | OTV_Validator otvalid ) |
| 138 | { |
| 139 | FT_Bytes p = table; |
| 140 | FT_UInt SubstFormat; |
| 141 | |
| 142 | |
| 143 | OTV_NAME_ENTER( "MultipleSubst" ); |
| 144 | |
| 145 | OTV_LIMIT_CHECK( 2 ); |
| 146 | SubstFormat = FT_NEXT_USHORT( p ); |
| 147 | |
| 148 | OTV_TRACE(( " (format %d)\n" , SubstFormat )); |
| 149 | |
| 150 | switch ( SubstFormat ) |
| 151 | { |
| 152 | case 1: |
| 153 | otvalid->extra1 = otvalid->glyph_count; |
| 154 | OTV_NEST2( MultipleSubstFormat1, Sequence ); |
| 155 | OTV_RUN( table, otvalid ); |
| 156 | break; |
| 157 | |
| 158 | default: |
| 159 | FT_INVALID_FORMAT; |
| 160 | } |
| 161 | |
| 162 | OTV_EXIT; |
| 163 | } |
| 164 | |
| 165 | |
| 166 | /*************************************************************************/ |
| 167 | /*************************************************************************/ |
| 168 | /***** *****/ |
| 169 | /***** GSUB LOOKUP TYPE 3 *****/ |
| 170 | /***** *****/ |
| 171 | /*************************************************************************/ |
| 172 | /*************************************************************************/ |
| 173 | |
| 174 | /* sets otvalid->extra1 (glyph count) */ |
| 175 | |
| 176 | static void |
| 177 | otv_AlternateSubst_validate( FT_Bytes table, |
| 178 | OTV_Validator otvalid ) |
| 179 | { |
| 180 | FT_Bytes p = table; |
| 181 | FT_UInt SubstFormat; |
| 182 | |
| 183 | |
| 184 | OTV_NAME_ENTER( "AlternateSubst" ); |
| 185 | |
| 186 | OTV_LIMIT_CHECK( 2 ); |
| 187 | SubstFormat = FT_NEXT_USHORT( p ); |
| 188 | |
| 189 | OTV_TRACE(( " (format %d)\n" , SubstFormat )); |
| 190 | |
| 191 | switch ( SubstFormat ) |
| 192 | { |
| 193 | case 1: |
| 194 | otvalid->extra1 = otvalid->glyph_count; |
| 195 | OTV_NEST2( AlternateSubstFormat1, AlternateSet ); |
| 196 | OTV_RUN( table, otvalid ); |
| 197 | break; |
| 198 | |
| 199 | default: |
| 200 | FT_INVALID_FORMAT; |
| 201 | } |
| 202 | |
| 203 | OTV_EXIT; |
| 204 | } |
| 205 | |
| 206 | |
| 207 | /*************************************************************************/ |
| 208 | /*************************************************************************/ |
| 209 | /***** *****/ |
| 210 | /***** GSUB LOOKUP TYPE 4 *****/ |
| 211 | /***** *****/ |
| 212 | /*************************************************************************/ |
| 213 | /*************************************************************************/ |
| 214 | |
| 215 | #define LigatureFunc otv_Ligature_validate |
| 216 | |
| 217 | /* uses otvalid->glyph_count */ |
| 218 | |
| 219 | static void |
| 220 | otv_Ligature_validate( FT_Bytes table, |
| 221 | OTV_Validator otvalid ) |
| 222 | { |
| 223 | FT_Bytes p = table; |
| 224 | FT_UInt LigatureGlyph, CompCount; |
| 225 | |
| 226 | |
| 227 | OTV_ENTER; |
| 228 | |
| 229 | OTV_LIMIT_CHECK( 4 ); |
| 230 | LigatureGlyph = FT_NEXT_USHORT( p ); |
| 231 | if ( LigatureGlyph >= otvalid->glyph_count ) |
| 232 | FT_INVALID_DATA; |
| 233 | |
| 234 | CompCount = FT_NEXT_USHORT( p ); |
| 235 | |
| 236 | OTV_TRACE(( " (CompCount = %d)\n" , CompCount )); |
| 237 | |
| 238 | if ( CompCount == 0 ) |
| 239 | FT_INVALID_DATA; |
| 240 | |
| 241 | CompCount--; |
| 242 | |
| 243 | OTV_LIMIT_CHECK( CompCount * 2 ); /* Component */ |
| 244 | |
| 245 | /* no need to check the Component glyph indices */ |
| 246 | |
| 247 | OTV_EXIT; |
| 248 | } |
| 249 | |
| 250 | |
| 251 | static void |
| 252 | otv_LigatureSubst_validate( FT_Bytes table, |
| 253 | OTV_Validator otvalid ) |
| 254 | { |
| 255 | FT_Bytes p = table; |
| 256 | FT_UInt SubstFormat; |
| 257 | |
| 258 | |
| 259 | OTV_NAME_ENTER( "LigatureSubst" ); |
| 260 | |
| 261 | OTV_LIMIT_CHECK( 2 ); |
| 262 | SubstFormat = FT_NEXT_USHORT( p ); |
| 263 | |
| 264 | OTV_TRACE(( " (format %d)\n" , SubstFormat )); |
| 265 | |
| 266 | switch ( SubstFormat ) |
| 267 | { |
| 268 | case 1: |
| 269 | OTV_NEST3( LigatureSubstFormat1, LigatureSet, Ligature ); |
| 270 | OTV_RUN( table, otvalid ); |
| 271 | break; |
| 272 | |
| 273 | default: |
| 274 | FT_INVALID_FORMAT; |
| 275 | } |
| 276 | |
| 277 | OTV_EXIT; |
| 278 | } |
| 279 | |
| 280 | |
| 281 | /*************************************************************************/ |
| 282 | /*************************************************************************/ |
| 283 | /***** *****/ |
| 284 | /***** GSUB LOOKUP TYPE 5 *****/ |
| 285 | /***** *****/ |
| 286 | /*************************************************************************/ |
| 287 | /*************************************************************************/ |
| 288 | |
| 289 | /* sets otvalid->extra1 (lookup count) */ |
| 290 | |
| 291 | static void |
| 292 | otv_ContextSubst_validate( FT_Bytes table, |
| 293 | OTV_Validator otvalid ) |
| 294 | { |
| 295 | FT_Bytes p = table; |
| 296 | FT_UInt SubstFormat; |
| 297 | |
| 298 | |
| 299 | OTV_NAME_ENTER( "ContextSubst" ); |
| 300 | |
| 301 | OTV_LIMIT_CHECK( 2 ); |
| 302 | SubstFormat = FT_NEXT_USHORT( p ); |
| 303 | |
| 304 | OTV_TRACE(( " (format %d)\n" , SubstFormat )); |
| 305 | |
| 306 | switch ( SubstFormat ) |
| 307 | { |
| 308 | case 1: |
| 309 | /* no need to check glyph indices/classes used as input for these */ |
| 310 | /* context rules since even invalid glyph indices/classes return */ |
| 311 | /* meaningful results */ |
| 312 | |
| 313 | otvalid->extra1 = otvalid->lookup_count; |
| 314 | OTV_NEST3( ContextSubstFormat1, SubRuleSet, SubRule ); |
| 315 | OTV_RUN( table, otvalid ); |
| 316 | break; |
| 317 | |
| 318 | case 2: |
| 319 | /* no need to check glyph indices/classes used as input for these */ |
| 320 | /* context rules since even invalid glyph indices/classes return */ |
| 321 | /* meaningful results */ |
| 322 | |
| 323 | OTV_NEST3( ContextSubstFormat2, SubClassSet, SubClassRule ); |
| 324 | OTV_RUN( table, otvalid ); |
| 325 | break; |
| 326 | |
| 327 | case 3: |
| 328 | OTV_NEST1( ContextSubstFormat3 ); |
| 329 | OTV_RUN( table, otvalid ); |
| 330 | break; |
| 331 | |
| 332 | default: |
| 333 | FT_INVALID_FORMAT; |
| 334 | } |
| 335 | |
| 336 | OTV_EXIT; |
| 337 | } |
| 338 | |
| 339 | |
| 340 | /*************************************************************************/ |
| 341 | /*************************************************************************/ |
| 342 | /***** *****/ |
| 343 | /***** GSUB LOOKUP TYPE 6 *****/ |
| 344 | /***** *****/ |
| 345 | /*************************************************************************/ |
| 346 | /*************************************************************************/ |
| 347 | |
| 348 | /* sets otvalid->extra1 (lookup count) */ |
| 349 | |
| 350 | static void |
| 351 | otv_ChainContextSubst_validate( FT_Bytes table, |
| 352 | OTV_Validator otvalid ) |
| 353 | { |
| 354 | FT_Bytes p = table; |
| 355 | FT_UInt SubstFormat; |
| 356 | |
| 357 | |
| 358 | OTV_NAME_ENTER( "ChainContextSubst" ); |
| 359 | |
| 360 | OTV_LIMIT_CHECK( 2 ); |
| 361 | SubstFormat = FT_NEXT_USHORT( p ); |
| 362 | |
| 363 | OTV_TRACE(( " (format %d)\n" , SubstFormat )); |
| 364 | |
| 365 | switch ( SubstFormat ) |
| 366 | { |
| 367 | case 1: |
| 368 | /* no need to check glyph indices/classes used as input for these */ |
| 369 | /* context rules since even invalid glyph indices/classes return */ |
| 370 | /* meaningful results */ |
| 371 | |
| 372 | otvalid->extra1 = otvalid->lookup_count; |
| 373 | OTV_NEST3( ChainContextSubstFormat1, |
| 374 | ChainSubRuleSet, ChainSubRule ); |
| 375 | OTV_RUN( table, otvalid ); |
| 376 | break; |
| 377 | |
| 378 | case 2: |
| 379 | /* no need to check glyph indices/classes used as input for these */ |
| 380 | /* context rules since even invalid glyph indices/classes return */ |
| 381 | /* meaningful results */ |
| 382 | |
| 383 | OTV_NEST3( ChainContextSubstFormat2, |
| 384 | ChainSubClassSet, ChainSubClassRule ); |
| 385 | OTV_RUN( table, otvalid ); |
| 386 | break; |
| 387 | |
| 388 | case 3: |
| 389 | OTV_NEST1( ChainContextSubstFormat3 ); |
| 390 | OTV_RUN( table, otvalid ); |
| 391 | break; |
| 392 | |
| 393 | default: |
| 394 | FT_INVALID_FORMAT; |
| 395 | } |
| 396 | |
| 397 | OTV_EXIT; |
| 398 | } |
| 399 | |
| 400 | |
| 401 | /*************************************************************************/ |
| 402 | /*************************************************************************/ |
| 403 | /***** *****/ |
| 404 | /***** GSUB LOOKUP TYPE 7 *****/ |
| 405 | /***** *****/ |
| 406 | /*************************************************************************/ |
| 407 | /*************************************************************************/ |
| 408 | |
| 409 | /* uses otvalid->type_funcs */ |
| 410 | |
| 411 | static void |
| 412 | otv_ExtensionSubst_validate( FT_Bytes table, |
| 413 | OTV_Validator otvalid ) |
| 414 | { |
| 415 | FT_Bytes p = table; |
| 416 | FT_UInt SubstFormat; |
| 417 | |
| 418 | |
| 419 | OTV_NAME_ENTER( "ExtensionSubst" ); |
| 420 | |
| 421 | OTV_LIMIT_CHECK( 2 ); |
| 422 | SubstFormat = FT_NEXT_USHORT( p ); |
| 423 | |
| 424 | OTV_TRACE(( " (format %d)\n" , SubstFormat )); |
| 425 | |
| 426 | switch ( SubstFormat ) |
| 427 | { |
| 428 | case 1: /* ExtensionSubstFormat1 */ |
| 429 | { |
| 430 | FT_UInt ExtensionLookupType; |
| 431 | FT_ULong ExtensionOffset; |
| 432 | OTV_Validate_Func validate; |
| 433 | |
| 434 | |
| 435 | OTV_LIMIT_CHECK( 6 ); |
| 436 | ExtensionLookupType = FT_NEXT_USHORT( p ); |
| 437 | ExtensionOffset = FT_NEXT_ULONG( p ); |
| 438 | |
| 439 | if ( ExtensionLookupType == 0 || |
| 440 | ExtensionLookupType == 7 || |
| 441 | ExtensionLookupType > 8 ) |
| 442 | FT_INVALID_DATA; |
| 443 | |
| 444 | validate = otvalid->type_funcs[ExtensionLookupType - 1]; |
| 445 | validate( table + ExtensionOffset, otvalid ); |
| 446 | } |
| 447 | break; |
| 448 | |
| 449 | default: |
| 450 | FT_INVALID_FORMAT; |
| 451 | } |
| 452 | |
| 453 | OTV_EXIT; |
| 454 | } |
| 455 | |
| 456 | |
| 457 | /*************************************************************************/ |
| 458 | /*************************************************************************/ |
| 459 | /***** *****/ |
| 460 | /***** GSUB LOOKUP TYPE 8 *****/ |
| 461 | /***** *****/ |
| 462 | /*************************************************************************/ |
| 463 | /*************************************************************************/ |
| 464 | |
| 465 | /* uses otvalid->glyph_count */ |
| 466 | |
| 467 | static void |
| 468 | otv_ReverseChainSingleSubst_validate( FT_Bytes table, |
| 469 | OTV_Validator otvalid ) |
| 470 | { |
| 471 | FT_Bytes p = table, Coverage; |
| 472 | FT_UInt SubstFormat; |
| 473 | FT_UInt BacktrackGlyphCount, LookaheadGlyphCount, GlyphCount; |
| 474 | |
| 475 | |
| 476 | OTV_NAME_ENTER( "ReverseChainSingleSubst" ); |
| 477 | |
| 478 | OTV_LIMIT_CHECK( 2 ); |
| 479 | SubstFormat = FT_NEXT_USHORT( p ); |
| 480 | |
| 481 | OTV_TRACE(( " (format %d)\n" , SubstFormat )); |
| 482 | |
| 483 | switch ( SubstFormat ) |
| 484 | { |
| 485 | case 1: /* ReverseChainSingleSubstFormat1 */ |
| 486 | OTV_LIMIT_CHECK( 4 ); |
| 487 | Coverage = table + FT_NEXT_USHORT( p ); |
| 488 | BacktrackGlyphCount = FT_NEXT_USHORT( p ); |
| 489 | |
| 490 | OTV_TRACE(( " (BacktrackGlyphCount = %d)\n" , BacktrackGlyphCount )); |
| 491 | |
| 492 | otv_Coverage_validate( Coverage, otvalid, -1 ); |
| 493 | |
| 494 | OTV_LIMIT_CHECK( BacktrackGlyphCount * 2 + 2 ); |
| 495 | |
| 496 | for ( ; BacktrackGlyphCount > 0; BacktrackGlyphCount-- ) |
| 497 | otv_Coverage_validate( table + FT_NEXT_USHORT( p ), otvalid, -1 ); |
| 498 | |
| 499 | LookaheadGlyphCount = FT_NEXT_USHORT( p ); |
| 500 | |
| 501 | OTV_TRACE(( " (LookaheadGlyphCount = %d)\n" , LookaheadGlyphCount )); |
| 502 | |
| 503 | OTV_LIMIT_CHECK( LookaheadGlyphCount * 2 + 2 ); |
| 504 | |
| 505 | for ( ; LookaheadGlyphCount > 0; LookaheadGlyphCount-- ) |
| 506 | otv_Coverage_validate( table + FT_NEXT_USHORT( p ), otvalid, -1 ); |
| 507 | |
| 508 | GlyphCount = FT_NEXT_USHORT( p ); |
| 509 | |
| 510 | OTV_TRACE(( " (GlyphCount = %d)\n" , GlyphCount )); |
| 511 | |
| 512 | if ( GlyphCount != otv_Coverage_get_count( Coverage ) ) |
| 513 | FT_INVALID_DATA; |
| 514 | |
| 515 | OTV_LIMIT_CHECK( GlyphCount * 2 ); |
| 516 | |
| 517 | /* Substitute */ |
| 518 | for ( ; GlyphCount > 0; GlyphCount-- ) |
| 519 | if ( FT_NEXT_USHORT( p ) >= otvalid->glyph_count ) |
| 520 | FT_INVALID_DATA; |
| 521 | |
| 522 | break; |
| 523 | |
| 524 | default: |
| 525 | FT_INVALID_FORMAT; |
| 526 | } |
| 527 | |
| 528 | OTV_EXIT; |
| 529 | } |
| 530 | |
| 531 | |
| 532 | static const OTV_Validate_Func otv_gsub_validate_funcs[8] = |
| 533 | { |
| 534 | otv_SingleSubst_validate, |
| 535 | otv_MultipleSubst_validate, |
| 536 | otv_AlternateSubst_validate, |
| 537 | otv_LigatureSubst_validate, |
| 538 | otv_ContextSubst_validate, |
| 539 | otv_ChainContextSubst_validate, |
| 540 | otv_ExtensionSubst_validate, |
| 541 | otv_ReverseChainSingleSubst_validate |
| 542 | }; |
| 543 | |
| 544 | |
| 545 | /*************************************************************************/ |
| 546 | /*************************************************************************/ |
| 547 | /***** *****/ |
| 548 | /***** GSUB TABLE *****/ |
| 549 | /***** *****/ |
| 550 | /*************************************************************************/ |
| 551 | /*************************************************************************/ |
| 552 | |
| 553 | /* sets otvalid->type_count */ |
| 554 | /* sets otvalid->type_funcs */ |
| 555 | /* sets otvalid->glyph_count */ |
| 556 | |
| 557 | FT_LOCAL_DEF( void ) |
| 558 | otv_GSUB_validate( FT_Bytes table, |
| 559 | FT_UInt glyph_count, |
| 560 | FT_Validator ftvalid ) |
| 561 | { |
| 562 | OTV_ValidatorRec otvalidrec; |
| 563 | OTV_Validator otvalid = &otvalidrec; |
| 564 | FT_Bytes p = table; |
| 565 | FT_UInt table_size; |
| 566 | FT_UShort version; |
| 567 | FT_UInt ScriptList, FeatureList, LookupList; |
| 568 | |
| 569 | OTV_OPTIONAL_TABLE32( featureVariations ); |
| 570 | |
| 571 | |
| 572 | otvalid->root = ftvalid; |
| 573 | |
| 574 | FT_TRACE3(( "validating GSUB table\n" )); |
| 575 | OTV_INIT; |
| 576 | |
| 577 | OTV_LIMIT_CHECK( 4 ); |
| 578 | |
| 579 | if ( FT_NEXT_USHORT( p ) != 1 ) /* majorVersion */ |
| 580 | FT_INVALID_FORMAT; |
| 581 | |
| 582 | version = FT_NEXT_USHORT( p ); /* minorVersion */ |
| 583 | |
| 584 | table_size = 10; |
| 585 | switch ( version ) |
| 586 | { |
| 587 | case 0: |
| 588 | OTV_LIMIT_CHECK( 6 ); |
| 589 | break; |
| 590 | |
| 591 | case 1: |
| 592 | OTV_LIMIT_CHECK( 10 ); |
| 593 | table_size += 4; |
| 594 | break; |
| 595 | |
| 596 | default: |
| 597 | FT_INVALID_FORMAT; |
| 598 | } |
| 599 | |
| 600 | ScriptList = FT_NEXT_USHORT( p ); |
| 601 | FeatureList = FT_NEXT_USHORT( p ); |
| 602 | LookupList = FT_NEXT_USHORT( p ); |
| 603 | |
| 604 | otvalid->type_count = 8; |
| 605 | otvalid->type_funcs = (OTV_Validate_Func*)otv_gsub_validate_funcs; |
| 606 | otvalid->glyph_count = glyph_count; |
| 607 | |
| 608 | otv_LookupList_validate( table + LookupList, |
| 609 | otvalid ); |
| 610 | otv_FeatureList_validate( table + FeatureList, table + LookupList, |
| 611 | otvalid ); |
| 612 | otv_ScriptList_validate( table + ScriptList, table + FeatureList, |
| 613 | otvalid ); |
| 614 | |
| 615 | if ( version > 0 ) |
| 616 | { |
| 617 | OTV_OPTIONAL_OFFSET32( featureVariations ); |
| 618 | OTV_SIZE_CHECK32( featureVariations ); |
| 619 | if ( featureVariations ) |
| 620 | OTV_TRACE(( " [omitting featureVariations validation]\n" )); /* XXX */ |
| 621 | } |
| 622 | |
| 623 | FT_TRACE4(( "\n" )); |
| 624 | } |
| 625 | |
| 626 | |
| 627 | /* END */ |
| 628 | |