hb-ot-layout-gsubgpos-private.hh revision b961518b9611471ff7060e97686e5625974847eb
1/* 2 * Copyright (C) 2007,2008,2009,2010 Red Hat, Inc. 3 * 4 * This is part of HarfBuzz, a text shaping library. 5 * 6 * Permission is hereby granted, without written agreement and without 7 * license or royalty fees, to use, copy, modify, and distribute this 8 * software and its documentation for any purpose, provided that the 9 * above copyright notice and the following two paragraphs appear in 10 * all copies of this software. 11 * 12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR 13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN 15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 16 * DAMAGE. 17 * 18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO 22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 23 * 24 * Red Hat Author(s): Behdad Esfahbod 25 */ 26 27#ifndef HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH 28#define HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH 29 30#include "hb-buffer-private.h" 31#include "hb-ot-layout-gdef-private.hh" 32 33 34#ifndef HB_DEBUG_APPLY 35#define HB_DEBUG_APPLY HB_DEBUG+0 36#endif 37 38#define TRACE_APPLY() \ 39 hb_trace_t<HB_DEBUG_APPLY> trace (&context->debug_depth); \ 40 trace.log ("APPLY", HB_FUNC, this); 41 42 43struct hb_apply_context_t 44{ 45 unsigned int debug_depth; 46 hb_ot_layout_context_t *layout; 47 hb_buffer_t *buffer; 48 unsigned int context_length; 49 unsigned int nesting_level_left; 50 unsigned int lookup_flag; 51 unsigned int property; /* propety of first glyph (TODO remove) */ 52}; 53 54 55 56 57#undef BUFFER 58#define BUFFER context->buffer 59 60 61typedef bool (*match_func_t) (hb_codepoint_t glyph_id, const USHORT &value, const void *data); 62typedef bool (*apply_lookup_func_t) (hb_apply_context_t *context, unsigned int lookup_index); 63 64struct ContextFuncs 65{ 66 match_func_t match; 67 apply_lookup_func_t apply; 68}; 69 70 71static inline bool match_glyph (hb_codepoint_t glyph_id, const USHORT &value, const void *data HB_UNUSED) 72{ 73 return glyph_id == value; 74} 75 76static inline bool match_class (hb_codepoint_t glyph_id, const USHORT &value, const void *data) 77{ 78 const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data); 79 return class_def.get_class (glyph_id) == value; 80} 81 82static inline bool match_coverage (hb_codepoint_t glyph_id, const USHORT &value, const void *data) 83{ 84 const OffsetTo<Coverage> &coverage = (const OffsetTo<Coverage>&)value; 85 return (data+coverage) (glyph_id) != NOT_COVERED; 86} 87 88 89static inline bool match_input (hb_apply_context_t *context, 90 unsigned int count, /* Including the first glyph (not matched) */ 91 const USHORT input[], /* Array of input values--start with second glyph */ 92 match_func_t match_func, 93 const void *match_data, 94 unsigned int *context_length_out) 95{ 96 unsigned int i, j; 97 unsigned int end = MIN (context->buffer->in_length, context->buffer->in_pos + context->context_length); 98 if (unlikely (context->buffer->in_pos + count > end)) 99 return false; 100 101 for (i = 1, j = context->buffer->in_pos + 1; i < count; i++, j++) 102 { 103 while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), context->lookup_flag, NULL)) 104 { 105 if (unlikely (j + count - i == end)) 106 return false; 107 j++; 108 } 109 110 if (likely (!match_func (IN_GLYPH (j), input[i - 1], match_data))) 111 return false; 112 } 113 114 *context_length_out = j - context->buffer->in_pos; 115 116 return true; 117} 118 119static inline bool match_backtrack (hb_apply_context_t *context, 120 unsigned int count, 121 const USHORT backtrack[], 122 match_func_t match_func, 123 const void *match_data) 124{ 125 if (unlikely (context->buffer->out_pos < count)) 126 return false; 127 128 for (unsigned int i = 0, j = context->buffer->out_pos - 1; i < count; i++, j--) 129 { 130 while (_hb_ot_layout_skip_mark (context->layout->face, OUT_INFO (j), context->lookup_flag, NULL)) 131 { 132 if (unlikely (j + 1 == count - i)) 133 return false; 134 j--; 135 } 136 137 if (likely (!match_func (OUT_GLYPH (j), backtrack[i], match_data))) 138 return false; 139 } 140 141 return true; 142} 143 144static inline bool match_lookahead (hb_apply_context_t *context, 145 unsigned int count, 146 const USHORT lookahead[], 147 match_func_t match_func, 148 const void *match_data, 149 unsigned int offset) 150{ 151 unsigned int i, j; 152 unsigned int end = MIN (context->buffer->in_length, context->buffer->in_pos + context->context_length); 153 if (unlikely (context->buffer->in_pos + offset + count > end)) 154 return false; 155 156 for (i = 0, j = context->buffer->in_pos + offset; i < count; i++, j++) 157 { 158 while (_hb_ot_layout_skip_mark (context->layout->face, OUT_INFO (j), context->lookup_flag, NULL)) 159 { 160 if (unlikely (j + count - i == end)) 161 return false; 162 j++; 163 } 164 165 if (likely (!match_func (IN_GLYPH (j), lookahead[i], match_data))) 166 return false; 167 } 168 169 return true; 170} 171 172 173struct LookupRecord 174{ 175 inline bool sanitize (hb_sanitize_context_t *context) { 176 TRACE_SANITIZE (); 177 return context->check_struct (this); 178 } 179 180 USHORT sequenceIndex; /* Index into current glyph 181 * sequence--first glyph = 0 */ 182 USHORT lookupListIndex; /* Lookup to apply to that 183 * position--zero--based */ 184 public: 185 DEFINE_SIZE_STATIC (4); 186}; 187 188static inline bool apply_lookup (hb_apply_context_t *context, 189 unsigned int count, /* Including the first glyph */ 190 unsigned int lookupCount, 191 const LookupRecord lookupRecord[], /* Array of LookupRecords--in design order */ 192 apply_lookup_func_t apply_func) 193{ 194 unsigned int end = MIN (context->buffer->in_length, context->buffer->in_pos + context->context_length); 195 if (unlikely (context->buffer->in_pos + count > end)) 196 return false; 197 198 /* TODO We don't support lookupRecord arrays that are not increasing: 199 * Should be easy for in_place ones at least. */ 200 201 /* Note: If sublookup is reverse, i will underflow after the first loop 202 * and we jump out of it. Not entirely disastrous. So we don't check 203 * for reverse lookup here. 204 */ 205 for (unsigned int i = 0; i < count; /* NOP */) 206 { 207 while (_hb_ot_layout_skip_mark (context->layout->face, IN_CURINFO (), context->lookup_flag, NULL)) 208 { 209 if (unlikely (context->buffer->in_pos == end)) 210 return true; 211 /* No lookup applied for this index */ 212 _hb_buffer_next_glyph (context->buffer); 213 } 214 215 if (lookupCount && i == lookupRecord->sequenceIndex) 216 { 217 unsigned int old_pos = context->buffer->in_pos; 218 219 /* Apply a lookup */ 220 bool done = apply_func (context, lookupRecord->lookupListIndex); 221 222 lookupRecord++; 223 lookupCount--; 224 /* Err, this is wrong if the lookup jumped over some glyphs */ 225 i += context->buffer->in_pos - old_pos; 226 if (unlikely (context->buffer->in_pos == end)) 227 return true; 228 229 if (!done) 230 goto not_applied; 231 } 232 else 233 { 234 not_applied: 235 /* No lookup applied for this index */ 236 _hb_buffer_next_glyph (context->buffer); 237 i++; 238 } 239 } 240 241 return true; 242} 243 244 245/* Contextual lookups */ 246 247struct ContextLookupContext 248{ 249 ContextFuncs funcs; 250 const void *match_data; 251}; 252 253static inline bool context_lookup (hb_apply_context_t *context, 254 unsigned int inputCount, /* Including the first glyph (not matched) */ 255 const USHORT input[], /* Array of input values--start with second glyph */ 256 unsigned int lookupCount, 257 const LookupRecord lookupRecord[], 258 ContextLookupContext &lookup_context) 259{ 260 hb_apply_context_t new_context = *context; 261 return match_input (context, 262 inputCount, input, 263 lookup_context.funcs.match, lookup_context.match_data, 264 &new_context.context_length) 265 && apply_lookup (&new_context, 266 inputCount, 267 lookupCount, lookupRecord, 268 lookup_context.funcs.apply); 269} 270 271struct Rule 272{ 273 friend struct RuleSet; 274 275 private: 276 inline bool apply (hb_apply_context_t *context, ContextLookupContext &lookup_context) const 277 { 278 TRACE_APPLY (); 279 const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].static_size * (inputCount ? inputCount - 1 : 0)); 280 return context_lookup (context, 281 inputCount, input, 282 lookupCount, lookupRecord, 283 lookup_context); 284 } 285 286 public: 287 inline bool sanitize (hb_sanitize_context_t *context) { 288 TRACE_SANITIZE (); 289 return inputCount.sanitize (context) 290 && lookupCount.sanitize (context) 291 && context->check_range (input, 292 input[0].static_size * inputCount 293 + lookupRecordX[0].static_size * lookupCount); 294 } 295 296 private: 297 USHORT inputCount; /* Total number of glyphs in input 298 * glyph sequence--includes the first 299 * glyph */ 300 USHORT lookupCount; /* Number of LookupRecords */ 301 USHORT input[VAR]; /* Array of match inputs--start with 302 * second glyph */ 303 LookupRecord lookupRecordX[VAR]; /* Array of LookupRecords--in 304 * design order */ 305 public: 306 DEFINE_SIZE_VAR2 (4, USHORT, LookupRecord); 307}; 308 309struct RuleSet 310{ 311 inline bool apply (hb_apply_context_t *context, ContextLookupContext &lookup_context) const 312 { 313 TRACE_APPLY (); 314 unsigned int num_rules = rule.len; 315 for (unsigned int i = 0; i < num_rules; i++) 316 { 317 if ((this+rule[i]).apply (context, lookup_context)) 318 return true; 319 } 320 321 return false; 322 } 323 324 inline bool sanitize (hb_sanitize_context_t *context) { 325 TRACE_SANITIZE (); 326 return rule.sanitize (context, this); 327 } 328 329 private: 330 OffsetArrayOf<Rule> 331 rule; /* Array of Rule tables 332 * ordered by preference */ 333 public: 334 DEFINE_SIZE_VAR (2, OffsetTo<Rule>); 335}; 336 337 338struct ContextFormat1 339{ 340 friend struct Context; 341 342 private: 343 inline bool apply (hb_apply_context_t *context, apply_lookup_func_t apply_func) const 344 { 345 TRACE_APPLY (); 346 unsigned int index = (this+coverage) (IN_CURGLYPH ()); 347 if (likely (index == NOT_COVERED)) 348 return false; 349 350 const RuleSet &rule_set = this+ruleSet[index]; 351 struct ContextLookupContext lookup_context = { 352 {match_glyph, apply_func}, 353 NULL 354 }; 355 return rule_set.apply (context, lookup_context); 356 } 357 358 inline bool sanitize (hb_sanitize_context_t *context) { 359 TRACE_SANITIZE (); 360 return coverage.sanitize (context, this) 361 && ruleSet.sanitize (context, this); 362 } 363 364 private: 365 USHORT format; /* Format identifier--format = 1 */ 366 OffsetTo<Coverage> 367 coverage; /* Offset to Coverage table--from 368 * beginning of table */ 369 OffsetArrayOf<RuleSet> 370 ruleSet; /* Array of RuleSet tables 371 * ordered by Coverage Index */ 372 public: 373 DEFINE_SIZE_VAR (6, OffsetTo<RuleSet>); 374}; 375 376 377struct ContextFormat2 378{ 379 friend struct Context; 380 381 private: 382 inline bool apply (hb_apply_context_t *context, apply_lookup_func_t apply_func) const 383 { 384 TRACE_APPLY (); 385 unsigned int index = (this+coverage) (IN_CURGLYPH ()); 386 if (likely (index == NOT_COVERED)) 387 return false; 388 389 const ClassDef &class_def = this+classDef; 390 index = class_def (IN_CURGLYPH ()); 391 const RuleSet &rule_set = this+ruleSet[index]; 392 /* LONGTERMTODO: Old code fetches glyph classes at most once and caches 393 * them across subrule lookups. Not sure it's worth it. 394 */ 395 struct ContextLookupContext lookup_context = { 396 {match_class, apply_func}, 397 &class_def 398 }; 399 return rule_set.apply (context, lookup_context); 400 } 401 402 inline bool sanitize (hb_sanitize_context_t *context) { 403 TRACE_SANITIZE (); 404 return coverage.sanitize (context, this) 405 && classDef.sanitize (context, this) 406 && ruleSet.sanitize (context, this); 407 } 408 409 private: 410 USHORT format; /* Format identifier--format = 2 */ 411 OffsetTo<Coverage> 412 coverage; /* Offset to Coverage table--from 413 * beginning of table */ 414 OffsetTo<ClassDef> 415 classDef; /* Offset to glyph ClassDef table--from 416 * beginning of table */ 417 OffsetArrayOf<RuleSet> 418 ruleSet; /* Array of RuleSet tables 419 * ordered by class */ 420 public: 421 DEFINE_SIZE_VAR (8, OffsetTo<RuleSet>); 422}; 423 424 425struct ContextFormat3 426{ 427 friend struct Context; 428 429 private: 430 inline bool apply (hb_apply_context_t *context, apply_lookup_func_t apply_func) const 431 { 432 TRACE_APPLY (); 433 unsigned int index = (this+coverage[0]) (IN_CURGLYPH ()); 434 if (likely (index == NOT_COVERED)) 435 return false; 436 437 const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * glyphCount); 438 struct ContextLookupContext lookup_context = { 439 {match_coverage, apply_func}, 440 this 441 }; 442 return context_lookup (context, 443 glyphCount, (const USHORT *) (coverage + 1), 444 lookupCount, lookupRecord, 445 lookup_context); 446 } 447 448 inline bool sanitize (hb_sanitize_context_t *context) { 449 TRACE_SANITIZE (); 450 if (!context->check_struct (this)) return false; 451 unsigned int count = glyphCount; 452 if (!context->check_array (coverage, coverage[0].static_size, count)) return false; 453 for (unsigned int i = 0; i < count; i++) 454 if (!coverage[i].sanitize (context, this)) return false; 455 LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * count); 456 return context->check_array (lookupRecord, lookupRecord[0].static_size, lookupCount); 457 } 458 459 private: 460 USHORT format; /* Format identifier--format = 3 */ 461 USHORT glyphCount; /* Number of glyphs in the input glyph 462 * sequence */ 463 USHORT lookupCount; /* Number of LookupRecords */ 464 OffsetTo<Coverage> 465 coverage[VAR]; /* Array of offsets to Coverage 466 * table in glyph sequence order */ 467 LookupRecord lookupRecordX[VAR]; /* Array of LookupRecords--in 468 * design order */ 469 public: 470 DEFINE_SIZE_VAR2 (6, OffsetTo<Coverage>, LookupRecord); 471}; 472 473struct Context 474{ 475 protected: 476 inline bool apply (hb_apply_context_t *context, apply_lookup_func_t apply_func) const 477 { 478 TRACE_APPLY (); 479 switch (u.format) { 480 case 1: return u.format1->apply (context, apply_func); 481 case 2: return u.format2->apply (context, apply_func); 482 case 3: return u.format3->apply (context, apply_func); 483 default:return false; 484 } 485 } 486 487 inline bool sanitize (hb_sanitize_context_t *context) { 488 TRACE_SANITIZE (); 489 if (!u.format.sanitize (context)) return false; 490 switch (u.format) { 491 case 1: return u.format1->sanitize (context); 492 case 2: return u.format2->sanitize (context); 493 case 3: return u.format3->sanitize (context); 494 default:return true; 495 } 496 } 497 498 private: 499 union { 500 USHORT format; /* Format identifier */ 501 ContextFormat1 format1[VAR]; 502 ContextFormat2 format2[VAR]; 503 ContextFormat3 format3[VAR]; 504 } u; 505}; 506 507 508/* Chaining Contextual lookups */ 509 510struct ChainContextLookupContext 511{ 512 ContextFuncs funcs; 513 const void *match_data[3]; 514}; 515 516static inline bool chain_context_lookup (hb_apply_context_t *context, 517 unsigned int backtrackCount, 518 const USHORT backtrack[], 519 unsigned int inputCount, /* Including the first glyph (not matched) */ 520 const USHORT input[], /* Array of input values--start with second glyph */ 521 unsigned int lookaheadCount, 522 const USHORT lookahead[], 523 unsigned int lookupCount, 524 const LookupRecord lookupRecord[], 525 ChainContextLookupContext &lookup_context) 526{ 527 /* First guess */ 528 if (unlikely (context->buffer->out_pos < backtrackCount || 529 context->buffer->in_pos + inputCount + lookaheadCount > context->buffer->in_length || 530 inputCount + lookaheadCount > context->context_length)) 531 return false; 532 533 hb_apply_context_t new_context = *context; 534 return match_backtrack (context, 535 backtrackCount, backtrack, 536 lookup_context.funcs.match, lookup_context.match_data[0]) 537 && match_input (context, 538 inputCount, input, 539 lookup_context.funcs.match, lookup_context.match_data[1], 540 &new_context.context_length) 541 && match_lookahead (context, 542 lookaheadCount, lookahead, 543 lookup_context.funcs.match, lookup_context.match_data[2], 544 new_context.context_length) 545 && apply_lookup (&new_context, 546 inputCount, 547 lookupCount, lookupRecord, 548 lookup_context.funcs.apply); 549} 550 551struct ChainRule 552{ 553 friend struct ChainRuleSet; 554 555 private: 556 inline bool apply (hb_apply_context_t *context, ChainContextLookupContext &lookup_context) const 557 { 558 TRACE_APPLY (); 559 const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack); 560 const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input); 561 const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead); 562 return chain_context_lookup (context, 563 backtrack.len, backtrack.array, 564 input.len, input.array, 565 lookahead.len, lookahead.array, 566 lookup.len, lookup.array, 567 lookup_context); 568 return false; 569 } 570 571 public: 572 inline bool sanitize (hb_sanitize_context_t *context) { 573 TRACE_SANITIZE (); 574 if (!backtrack.sanitize (context)) return false; 575 HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack); 576 if (!input.sanitize (context)) return false; 577 ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input); 578 if (!lookahead.sanitize (context)) return false; 579 ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead); 580 return lookup.sanitize (context); 581 } 582 583 private: 584 ArrayOf<USHORT> 585 backtrack; /* Array of backtracking values 586 * (to be matched before the input 587 * sequence) */ 588 HeadlessArrayOf<USHORT> 589 inputX; /* Array of input values (start with 590 * second glyph) */ 591 ArrayOf<USHORT> 592 lookaheadX; /* Array of lookahead values's (to be 593 * matched after the input sequence) */ 594 ArrayOf<LookupRecord> 595 lookupX; /* Array of LookupRecords--in 596 * design order) */ 597 public: 598 DEFINE_SIZE_MIN (8); 599}; 600 601struct ChainRuleSet 602{ 603 inline bool apply (hb_apply_context_t *context, ChainContextLookupContext &lookup_context) const 604 { 605 TRACE_APPLY (); 606 unsigned int num_rules = rule.len; 607 for (unsigned int i = 0; i < num_rules; i++) 608 { 609 if ((this+rule[i]).apply (context, lookup_context)) 610 return true; 611 } 612 613 return false; 614 } 615 616 inline bool sanitize (hb_sanitize_context_t *context) { 617 TRACE_SANITIZE (); 618 return rule.sanitize (context, this); 619 } 620 621 private: 622 OffsetArrayOf<ChainRule> 623 rule; /* Array of ChainRule tables 624 * ordered by preference */ 625 public: 626 DEFINE_SIZE_VAR (2, OffsetTo<ChainRule>); 627}; 628 629struct ChainContextFormat1 630{ 631 friend struct ChainContext; 632 633 private: 634 inline bool apply (hb_apply_context_t *context, apply_lookup_func_t apply_func) const 635 { 636 TRACE_APPLY (); 637 unsigned int index = (this+coverage) (IN_CURGLYPH ()); 638 if (likely (index == NOT_COVERED)) 639 return false; 640 641 const ChainRuleSet &rule_set = this+ruleSet[index]; 642 struct ChainContextLookupContext lookup_context = { 643 {match_glyph, apply_func}, 644 {NULL, NULL, NULL} 645 }; 646 return rule_set.apply (context, lookup_context); 647 } 648 649 inline bool sanitize (hb_sanitize_context_t *context) { 650 TRACE_SANITIZE (); 651 return coverage.sanitize (context, this) 652 && ruleSet.sanitize (context, this); 653 } 654 655 private: 656 USHORT format; /* Format identifier--format = 1 */ 657 OffsetTo<Coverage> 658 coverage; /* Offset to Coverage table--from 659 * beginning of table */ 660 OffsetArrayOf<ChainRuleSet> 661 ruleSet; /* Array of ChainRuleSet tables 662 * ordered by Coverage Index */ 663 public: 664 DEFINE_SIZE_VAR (6, OffsetTo<ChainRuleSet>); 665}; 666 667struct ChainContextFormat2 668{ 669 friend struct ChainContext; 670 671 private: 672 inline bool apply (hb_apply_context_t *context, apply_lookup_func_t apply_func) const 673 { 674 TRACE_APPLY (); 675 unsigned int index = (this+coverage) (IN_CURGLYPH ()); 676 if (likely (index == NOT_COVERED)) 677 return false; 678 679 const ClassDef &backtrack_class_def = this+backtrackClassDef; 680 const ClassDef &input_class_def = this+inputClassDef; 681 const ClassDef &lookahead_class_def = this+lookaheadClassDef; 682 683 index = input_class_def (IN_CURGLYPH ()); 684 const ChainRuleSet &rule_set = this+ruleSet[index]; 685 /* LONGTERMTODO: Old code fetches glyph classes at most once and caches 686 * them across subrule lookups. Not sure it's worth it. 687 */ 688 struct ChainContextLookupContext lookup_context = { 689 {match_class, apply_func}, 690 {&backtrack_class_def, 691 &input_class_def, 692 &lookahead_class_def} 693 }; 694 return rule_set.apply (context, lookup_context); 695 } 696 697 inline bool sanitize (hb_sanitize_context_t *context) { 698 TRACE_SANITIZE (); 699 return coverage.sanitize (context, this) 700 && backtrackClassDef.sanitize (context, this) 701 && inputClassDef.sanitize (context, this) 702 && lookaheadClassDef.sanitize (context, this) 703 && ruleSet.sanitize (context, this); 704 } 705 706 private: 707 USHORT format; /* Format identifier--format = 2 */ 708 OffsetTo<Coverage> 709 coverage; /* Offset to Coverage table--from 710 * beginning of table */ 711 OffsetTo<ClassDef> 712 backtrackClassDef; /* Offset to glyph ClassDef table 713 * containing backtrack sequence 714 * data--from beginning of table */ 715 OffsetTo<ClassDef> 716 inputClassDef; /* Offset to glyph ClassDef 717 * table containing input sequence 718 * data--from beginning of table */ 719 OffsetTo<ClassDef> 720 lookaheadClassDef; /* Offset to glyph ClassDef table 721 * containing lookahead sequence 722 * data--from beginning of table */ 723 OffsetArrayOf<ChainRuleSet> 724 ruleSet; /* Array of ChainRuleSet tables 725 * ordered by class */ 726 public: 727 DEFINE_SIZE_VAR (12, OffsetTo<ChainRuleSet>); 728}; 729 730struct ChainContextFormat3 731{ 732 friend struct ChainContext; 733 734 private: 735 736 inline bool apply (hb_apply_context_t *context, apply_lookup_func_t apply_func) const 737 { 738 TRACE_APPLY (); 739 const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack); 740 741 unsigned int index = (this+input[0]) (IN_CURGLYPH ()); 742 if (likely (index == NOT_COVERED)) 743 return false; 744 745 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input); 746 const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead); 747 struct ChainContextLookupContext lookup_context = { 748 {match_coverage, apply_func}, 749 {this, this, this} 750 }; 751 return chain_context_lookup (context, 752 backtrack.len, (const USHORT *) backtrack.array, 753 input.len, (const USHORT *) input.array + 1, 754 lookahead.len, (const USHORT *) lookahead.array, 755 lookup.len, lookup.array, 756 lookup_context); 757 return false; 758 } 759 760 inline bool sanitize (hb_sanitize_context_t *context) { 761 TRACE_SANITIZE (); 762 if (!backtrack.sanitize (context, this)) return false; 763 OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack); 764 if (!input.sanitize (context, this)) return false; 765 OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input); 766 if (!lookahead.sanitize (context, this)) return false; 767 ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead); 768 return lookup.sanitize (context); 769 } 770 771 private: 772 USHORT format; /* Format identifier--format = 3 */ 773 OffsetArrayOf<Coverage> 774 backtrack; /* Array of coverage tables 775 * in backtracking sequence, in glyph 776 * sequence order */ 777 OffsetArrayOf<Coverage> 778 inputX ; /* Array of coverage 779 * tables in input sequence, in glyph 780 * sequence order */ 781 OffsetArrayOf<Coverage> 782 lookaheadX; /* Array of coverage tables 783 * in lookahead sequence, in glyph 784 * sequence order */ 785 ArrayOf<LookupRecord> 786 lookupX; /* Array of LookupRecords--in 787 * design order) */ 788 public: 789 DEFINE_SIZE_MIN (10); 790}; 791 792struct ChainContext 793{ 794 protected: 795 inline bool apply (hb_apply_context_t *context, apply_lookup_func_t apply_func) const 796 { 797 TRACE_APPLY (); 798 switch (u.format) { 799 case 1: return u.format1->apply (context, apply_func); 800 case 2: return u.format2->apply (context, apply_func); 801 case 3: return u.format3->apply (context, apply_func); 802 default:return false; 803 } 804 } 805 806 inline bool sanitize (hb_sanitize_context_t *context) { 807 TRACE_SANITIZE (); 808 if (!u.format.sanitize (context)) return false; 809 switch (u.format) { 810 case 1: return u.format1->sanitize (context); 811 case 2: return u.format2->sanitize (context); 812 case 3: return u.format3->sanitize (context); 813 default:return true; 814 } 815 } 816 817 private: 818 union { 819 USHORT format; /* Format identifier */ 820 ChainContextFormat1 format1[VAR]; 821 ChainContextFormat2 format2[VAR]; 822 ChainContextFormat3 format3[VAR]; 823 } u; 824}; 825 826 827struct ExtensionFormat1 828{ 829 friend struct Extension; 830 831 protected: 832 inline unsigned int get_type (void) const { return extensionLookupType; } 833 inline unsigned int get_offset (void) const { return extensionOffset; } 834 835 inline bool sanitize (hb_sanitize_context_t *context) { 836 TRACE_SANITIZE (); 837 return context->check_struct (this); 838 } 839 840 private: 841 USHORT format; /* Format identifier. Set to 1. */ 842 USHORT extensionLookupType; /* Lookup type of subtable referenced 843 * by ExtensionOffset (i.e. the 844 * extension subtable). */ 845 ULONG extensionOffset; /* Offset to the extension subtable, 846 * of lookup type subtable. */ 847 public: 848 DEFINE_SIZE_STATIC (8); 849}; 850 851struct Extension 852{ 853 inline unsigned int get_type (void) const 854 { 855 switch (u.format) { 856 case 1: return u.format1->get_type (); 857 default:return 0; 858 } 859 } 860 inline unsigned int get_offset (void) const 861 { 862 switch (u.format) { 863 case 1: return u.format1->get_offset (); 864 default:return 0; 865 } 866 } 867 868 inline bool sanitize (hb_sanitize_context_t *context) { 869 TRACE_SANITIZE (); 870 if (!u.format.sanitize (context)) return false; 871 switch (u.format) { 872 case 1: return u.format1->sanitize (context); 873 default:return true; 874 } 875 } 876 877 private: 878 union { 879 USHORT format; /* Format identifier */ 880 ExtensionFormat1 format1[VAR]; 881 } u; 882}; 883 884 885/* 886 * GSUB/GPOS Common 887 */ 888 889struct GSUBGPOS 890{ 891 static const hb_tag_t GSUBTag = HB_OT_TAG_GSUB; 892 static const hb_tag_t GPOSTag = HB_OT_TAG_GPOS; 893 894 inline unsigned int get_script_count (void) const 895 { return (this+scriptList).len; } 896 inline const Tag& get_script_tag (unsigned int i) const 897 { return (this+scriptList).get_tag (i); } 898 inline unsigned int get_script_tags (unsigned int start_offset, 899 unsigned int *script_count /* IN/OUT */, 900 hb_tag_t *script_tags /* OUT */) const 901 { return (this+scriptList).get_tags (start_offset, script_count, script_tags); } 902 inline const Script& get_script (unsigned int i) const 903 { return (this+scriptList)[i]; } 904 inline bool find_script_index (hb_tag_t tag, unsigned int *index) const 905 { return (this+scriptList).find_index (tag, index); } 906 907 inline unsigned int get_feature_count (void) const 908 { return (this+featureList).len; } 909 inline const Tag& get_feature_tag (unsigned int i) const 910 { return (this+featureList).get_tag (i); } 911 inline unsigned int get_feature_tags (unsigned int start_offset, 912 unsigned int *feature_count /* IN/OUT */, 913 hb_tag_t *feature_tags /* OUT */) const 914 { return (this+featureList).get_tags (start_offset, feature_count, feature_tags); } 915 inline const Feature& get_feature (unsigned int i) const 916 { return (this+featureList)[i]; } 917 inline bool find_feature_index (hb_tag_t tag, unsigned int *index) const 918 { return (this+featureList).find_index (tag, index); } 919 920 inline unsigned int get_lookup_count (void) const 921 { return (this+lookupList).len; } 922 inline const Lookup& get_lookup (unsigned int i) const 923 { return (this+lookupList)[i]; } 924 925 inline bool sanitize (hb_sanitize_context_t *context) { 926 TRACE_SANITIZE (); 927 return version.sanitize (context) && likely (version.major == 1) 928 && scriptList.sanitize (context, this) 929 && featureList.sanitize (context, this) 930 && lookupList.sanitize (context, this); 931 } 932 933 protected: 934 FixedVersion version; /* Version of the GSUB/GPOS table--initially set 935 * to 0x00010000 */ 936 OffsetTo<ScriptList> 937 scriptList; /* ScriptList table */ 938 OffsetTo<FeatureList> 939 featureList; /* FeatureList table */ 940 OffsetTo<LookupList> 941 lookupList; /* LookupList table */ 942 public: 943 DEFINE_SIZE_STATIC (10); 944}; 945 946 947#endif /* HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH */ 948