hb-ot-layout-gsubgpos-private.hh revision 8eeed7eddc789151cbffe62ed6bfd77612266bf1
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.hh" 31#include "hb-ot-layout-gdef-private.hh" 32 33HB_BEGIN_DECLS 34 35 36#ifndef HB_DEBUG_APPLY 37#define HB_DEBUG_APPLY HB_DEBUG+0 38#endif 39 40#define TRACE_APPLY() \ 41 hb_trace_t<HB_DEBUG_APPLY> trace (&c->debug_depth, "APPLY", HB_FUNC, this); \ 42 43 44HB_BEGIN_DECLS 45 46struct hb_apply_context_t 47{ 48 unsigned int debug_depth; 49 hb_ot_layout_context_t *layout; 50 hb_buffer_t *buffer; 51 hb_mask_t lookup_mask; 52 unsigned int context_length; 53 unsigned int nesting_level_left; 54 unsigned int lookup_flag; 55 unsigned int property; /* propety of first glyph (TODO remove) */ 56}; 57 58 59 60typedef bool (*match_func_t) (hb_codepoint_t glyph_id, const USHORT &value, const void *data); 61typedef bool (*apply_lookup_func_t) (hb_apply_context_t *c, unsigned int lookup_index); 62 63struct ContextFuncs 64{ 65 match_func_t match; 66 apply_lookup_func_t apply; 67}; 68 69 70static inline bool match_glyph (hb_codepoint_t glyph_id, const USHORT &value, const void *data HB_UNUSED) 71{ 72 return glyph_id == value; 73} 74 75static inline bool match_class (hb_codepoint_t glyph_id, const USHORT &value, const void *data) 76{ 77 const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data); 78 return class_def.get_class (glyph_id) == value; 79} 80 81static inline bool match_coverage (hb_codepoint_t glyph_id, const USHORT &value, const void *data) 82{ 83 const OffsetTo<Coverage> &coverage = (const OffsetTo<Coverage>&)value; 84 return (data+coverage) (glyph_id) != NOT_COVERED; 85} 86 87 88static inline bool match_input (hb_apply_context_t *c, 89 unsigned int count, /* Including the first glyph (not matched) */ 90 const USHORT input[], /* Array of input values--start with second glyph */ 91 match_func_t match_func, 92 const void *match_data, 93 unsigned int *context_length_out) 94{ 95 unsigned int i, j; 96 unsigned int end = MIN (c->buffer->len, c->buffer->i + c->context_length); 97 if (unlikely (c->buffer->i + count > end)) 98 return false; 99 100 for (i = 1, j = c->buffer->i + 1; i < count; i++, j++) 101 { 102 while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[j], c->lookup_flag, NULL)) 103 { 104 if (unlikely (j + count - i == end)) 105 return false; 106 j++; 107 } 108 109 if (likely (!match_func (c->buffer->info[j].codepoint, input[i - 1], match_data))) 110 return false; 111 } 112 113 *context_length_out = j - c->buffer->i; 114 115 return true; 116} 117 118static inline bool match_backtrack (hb_apply_context_t *c, 119 unsigned int count, 120 const USHORT backtrack[], 121 match_func_t match_func, 122 const void *match_data) 123{ 124 if (unlikely (c->buffer->out_len < count)) 125 return false; 126 127 for (unsigned int i = 0, j = c->buffer->out_len - 1; i < count; i++, j--) 128 { 129 while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->out_info[j], c->lookup_flag, NULL)) 130 { 131 if (unlikely (j + 1 == count - i)) 132 return false; 133 j--; 134 } 135 136 if (likely (!match_func (c->buffer->out_info[j].codepoint, backtrack[i], match_data))) 137 return false; 138 } 139 140 return true; 141} 142 143static inline bool match_lookahead (hb_apply_context_t *c, 144 unsigned int count, 145 const USHORT lookahead[], 146 match_func_t match_func, 147 const void *match_data, 148 unsigned int offset) 149{ 150 unsigned int i, j; 151 unsigned int end = MIN (c->buffer->len, c->buffer->i + c->context_length); 152 if (unlikely (c->buffer->i + offset + count > end)) 153 return false; 154 155 for (i = 0, j = c->buffer->i + offset; i < count; i++, j++) 156 { 157 while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[j], c->lookup_flag, NULL)) 158 { 159 if (unlikely (j + count - i == end)) 160 return false; 161 j++; 162 } 163 164 if (likely (!match_func (c->buffer->info[j].codepoint, lookahead[i], match_data))) 165 return false; 166 } 167 168 return true; 169} 170 171HB_END_DECLS 172 173 174struct LookupRecord 175{ 176 inline bool sanitize (hb_sanitize_context_t *c) { 177 TRACE_SANITIZE (); 178 return c->check_struct (this); 179 } 180 181 USHORT sequenceIndex; /* Index into current glyph 182 * sequence--first glyph = 0 */ 183 USHORT lookupListIndex; /* Lookup to apply to that 184 * position--zero--based */ 185 public: 186 DEFINE_SIZE_STATIC (4); 187}; 188 189 190HB_BEGIN_DECLS 191 192static inline bool apply_lookup (hb_apply_context_t *c, 193 unsigned int count, /* Including the first glyph */ 194 unsigned int lookupCount, 195 const LookupRecord lookupRecord[], /* Array of LookupRecords--in design order */ 196 apply_lookup_func_t apply_func) 197{ 198 unsigned int end = MIN (c->buffer->len, c->buffer->i + c->context_length); 199 if (unlikely (c->buffer->i + count > end)) 200 return false; 201 202 /* TODO We don't support lookupRecord arrays that are not increasing: 203 * Should be easy for in_place ones at least. */ 204 205 /* Note: If sublookup is reverse, i will underflow after the first loop 206 * and we jump out of it. Not entirely disastrous. So we don't check 207 * for reverse lookup here. 208 */ 209 for (unsigned int i = 0; i < count; /* NOP */) 210 { 211 while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[c->buffer->i], c->lookup_flag, NULL)) 212 { 213 if (unlikely (c->buffer->i == end)) 214 return true; 215 /* No lookup applied for this index */ 216 c->buffer->next_glyph (); 217 } 218 219 if (lookupCount && i == lookupRecord->sequenceIndex) 220 { 221 unsigned int old_pos = c->buffer->i; 222 223 /* Apply a lookup */ 224 bool done = apply_func (c, lookupRecord->lookupListIndex); 225 226 lookupRecord++; 227 lookupCount--; 228 /* Err, this is wrong if the lookup jumped over some glyphs */ 229 i += c->buffer->i - old_pos; 230 if (unlikely (c->buffer->i == end)) 231 return true; 232 233 if (!done) 234 goto not_applied; 235 } 236 else 237 { 238 not_applied: 239 /* No lookup applied for this index */ 240 c->buffer->next_glyph (); 241 i++; 242 } 243 } 244 245 return true; 246} 247 248HB_END_DECLS 249 250 251/* Contextual lookups */ 252 253struct ContextLookupContext 254{ 255 ContextFuncs funcs; 256 const void *match_data; 257}; 258 259static inline bool context_lookup (hb_apply_context_t *c, 260 unsigned int inputCount, /* Including the first glyph (not matched) */ 261 const USHORT input[], /* Array of input values--start with second glyph */ 262 unsigned int lookupCount, 263 const LookupRecord lookupRecord[], 264 ContextLookupContext &lookup_context) 265{ 266 hb_apply_context_t new_context = *c; 267 return match_input (c, 268 inputCount, input, 269 lookup_context.funcs.match, lookup_context.match_data, 270 &new_context.context_length) 271 && apply_lookup (&new_context, 272 inputCount, 273 lookupCount, lookupRecord, 274 lookup_context.funcs.apply); 275} 276 277struct Rule 278{ 279 friend struct RuleSet; 280 281 private: 282 inline bool apply (hb_apply_context_t *c, ContextLookupContext &lookup_context) const 283 { 284 TRACE_APPLY (); 285 const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].static_size * (inputCount ? inputCount - 1 : 0)); 286 return context_lookup (c, 287 inputCount, input, 288 lookupCount, lookupRecord, 289 lookup_context); 290 } 291 292 public: 293 inline bool sanitize (hb_sanitize_context_t *c) { 294 TRACE_SANITIZE (); 295 return inputCount.sanitize (c) 296 && lookupCount.sanitize (c) 297 && c->check_range (input, 298 input[0].static_size * inputCount 299 + lookupRecordX[0].static_size * lookupCount); 300 } 301 302 private: 303 USHORT inputCount; /* Total number of glyphs in input 304 * glyph sequence--includes the first 305 * glyph */ 306 USHORT lookupCount; /* Number of LookupRecords */ 307 USHORT input[VAR]; /* Array of match inputs--start with 308 * second glyph */ 309 LookupRecord lookupRecordX[VAR]; /* Array of LookupRecords--in 310 * design order */ 311 public: 312 DEFINE_SIZE_ARRAY2 (4, input, lookupRecordX); 313}; 314 315struct RuleSet 316{ 317 inline bool apply (hb_apply_context_t *c, ContextLookupContext &lookup_context) const 318 { 319 TRACE_APPLY (); 320 unsigned int num_rules = rule.len; 321 for (unsigned int i = 0; i < num_rules; i++) 322 { 323 if ((this+rule[i]).apply (c, lookup_context)) 324 return true; 325 } 326 327 return false; 328 } 329 330 inline bool sanitize (hb_sanitize_context_t *c) { 331 TRACE_SANITIZE (); 332 return rule.sanitize (c, this); 333 } 334 335 private: 336 OffsetArrayOf<Rule> 337 rule; /* Array of Rule tables 338 * ordered by preference */ 339 public: 340 DEFINE_SIZE_ARRAY (2, rule); 341}; 342 343 344struct ContextFormat1 345{ 346 friend struct Context; 347 348 private: 349 inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const 350 { 351 TRACE_APPLY (); 352 unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint); 353 if (likely (index == NOT_COVERED)) 354 return false; 355 356 const RuleSet &rule_set = this+ruleSet[index]; 357 struct ContextLookupContext lookup_context = { 358 {match_glyph, apply_func}, 359 NULL 360 }; 361 return rule_set.apply (c, lookup_context); 362 } 363 364 inline bool sanitize (hb_sanitize_context_t *c) { 365 TRACE_SANITIZE (); 366 return coverage.sanitize (c, this) 367 && ruleSet.sanitize (c, this); 368 } 369 370 private: 371 USHORT format; /* Format identifier--format = 1 */ 372 OffsetTo<Coverage> 373 coverage; /* Offset to Coverage table--from 374 * beginning of table */ 375 OffsetArrayOf<RuleSet> 376 ruleSet; /* Array of RuleSet tables 377 * ordered by Coverage Index */ 378 public: 379 DEFINE_SIZE_ARRAY (6, ruleSet); 380}; 381 382 383struct ContextFormat2 384{ 385 friend struct Context; 386 387 private: 388 inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const 389 { 390 TRACE_APPLY (); 391 unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint); 392 if (likely (index == NOT_COVERED)) 393 return false; 394 395 const ClassDef &class_def = this+classDef; 396 index = class_def (c->buffer->info[c->buffer->i].codepoint); 397 const RuleSet &rule_set = this+ruleSet[index]; 398 struct ContextLookupContext lookup_context = { 399 {match_class, apply_func}, 400 &class_def 401 }; 402 return rule_set.apply (c, lookup_context); 403 } 404 405 inline bool sanitize (hb_sanitize_context_t *c) { 406 TRACE_SANITIZE (); 407 return coverage.sanitize (c, this) 408 && classDef.sanitize (c, this) 409 && ruleSet.sanitize (c, this); 410 } 411 412 private: 413 USHORT format; /* Format identifier--format = 2 */ 414 OffsetTo<Coverage> 415 coverage; /* Offset to Coverage table--from 416 * beginning of table */ 417 OffsetTo<ClassDef> 418 classDef; /* Offset to glyph ClassDef table--from 419 * beginning of table */ 420 OffsetArrayOf<RuleSet> 421 ruleSet; /* Array of RuleSet tables 422 * ordered by class */ 423 public: 424 DEFINE_SIZE_ARRAY (8, ruleSet); 425}; 426 427 428struct ContextFormat3 429{ 430 friend struct Context; 431 432 private: 433 inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const 434 { 435 TRACE_APPLY (); 436 unsigned int index = (this+coverage[0]) (c->buffer->info[c->buffer->i].codepoint); 437 if (likely (index == NOT_COVERED)) 438 return false; 439 440 const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * glyphCount); 441 struct ContextLookupContext lookup_context = { 442 {match_coverage, apply_func}, 443 this 444 }; 445 return context_lookup (c, 446 glyphCount, (const USHORT *) (coverage + 1), 447 lookupCount, lookupRecord, 448 lookup_context); 449 } 450 451 inline bool sanitize (hb_sanitize_context_t *c) { 452 TRACE_SANITIZE (); 453 if (!c->check_struct (this)) return false; 454 unsigned int count = glyphCount; 455 if (!c->check_array (coverage, coverage[0].static_size, count)) return false; 456 for (unsigned int i = 0; i < count; i++) 457 if (!coverage[i].sanitize (c, this)) return false; 458 LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * count); 459 return c->check_array (lookupRecord, lookupRecord[0].static_size, lookupCount); 460 } 461 462 private: 463 USHORT format; /* Format identifier--format = 3 */ 464 USHORT glyphCount; /* Number of glyphs in the input glyph 465 * sequence */ 466 USHORT lookupCount; /* Number of LookupRecords */ 467 OffsetTo<Coverage> 468 coverage[VAR]; /* Array of offsets to Coverage 469 * table in glyph sequence order */ 470 LookupRecord lookupRecordX[VAR]; /* Array of LookupRecords--in 471 * design order */ 472 public: 473 DEFINE_SIZE_ARRAY2 (6, coverage, lookupRecordX); 474}; 475 476struct Context 477{ 478 protected: 479 inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const 480 { 481 TRACE_APPLY (); 482 switch (u.format) { 483 case 1: return u.format1.apply (c, apply_func); 484 case 2: return u.format2.apply (c, apply_func); 485 case 3: return u.format3.apply (c, apply_func); 486 default:return false; 487 } 488 } 489 490 inline bool sanitize (hb_sanitize_context_t *c) { 491 TRACE_SANITIZE (); 492 if (!u.format.sanitize (c)) return false; 493 switch (u.format) { 494 case 1: return u.format1.sanitize (c); 495 case 2: return u.format2.sanitize (c); 496 case 3: return u.format3.sanitize (c); 497 default:return true; 498 } 499 } 500 501 private: 502 union { 503 USHORT format; /* Format identifier */ 504 ContextFormat1 format1; 505 ContextFormat2 format2; 506 ContextFormat3 format3; 507 } u; 508}; 509 510 511/* Chaining Contextual lookups */ 512 513struct ChainContextLookupContext 514{ 515 ContextFuncs funcs; 516 const void *match_data[3]; 517}; 518 519static inline bool chain_context_lookup (hb_apply_context_t *c, 520 unsigned int backtrackCount, 521 const USHORT backtrack[], 522 unsigned int inputCount, /* Including the first glyph (not matched) */ 523 const USHORT input[], /* Array of input values--start with second glyph */ 524 unsigned int lookaheadCount, 525 const USHORT lookahead[], 526 unsigned int lookupCount, 527 const LookupRecord lookupRecord[], 528 ChainContextLookupContext &lookup_context) 529{ 530 /* First guess */ 531 if (unlikely (c->buffer->out_len < backtrackCount || 532 c->buffer->i + inputCount + lookaheadCount > c->buffer->len || 533 inputCount + lookaheadCount > c->context_length)) 534 return false; 535 536 hb_apply_context_t new_context = *c; 537 return match_backtrack (c, 538 backtrackCount, backtrack, 539 lookup_context.funcs.match, lookup_context.match_data[0]) 540 && match_input (c, 541 inputCount, input, 542 lookup_context.funcs.match, lookup_context.match_data[1], 543 &new_context.context_length) 544 && match_lookahead (c, 545 lookaheadCount, lookahead, 546 lookup_context.funcs.match, lookup_context.match_data[2], 547 new_context.context_length) 548 && apply_lookup (&new_context, 549 inputCount, 550 lookupCount, lookupRecord, 551 lookup_context.funcs.apply); 552} 553 554struct ChainRule 555{ 556 friend struct ChainRuleSet; 557 558 private: 559 inline bool apply (hb_apply_context_t *c, ChainContextLookupContext &lookup_context) const 560 { 561 TRACE_APPLY (); 562 const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack); 563 const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input); 564 const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead); 565 return chain_context_lookup (c, 566 backtrack.len, backtrack.array, 567 input.len, input.array, 568 lookahead.len, lookahead.array, 569 lookup.len, lookup.array, 570 lookup_context); 571 return false; 572 } 573 574 public: 575 inline bool sanitize (hb_sanitize_context_t *c) { 576 TRACE_SANITIZE (); 577 if (!backtrack.sanitize (c)) return false; 578 HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack); 579 if (!input.sanitize (c)) return false; 580 ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input); 581 if (!lookahead.sanitize (c)) return false; 582 ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead); 583 return lookup.sanitize (c); 584 } 585 586 private: 587 ArrayOf<USHORT> 588 backtrack; /* Array of backtracking values 589 * (to be matched before the input 590 * sequence) */ 591 HeadlessArrayOf<USHORT> 592 inputX; /* Array of input values (start with 593 * second glyph) */ 594 ArrayOf<USHORT> 595 lookaheadX; /* Array of lookahead values's (to be 596 * matched after the input sequence) */ 597 ArrayOf<LookupRecord> 598 lookupX; /* Array of LookupRecords--in 599 * design order) */ 600 public: 601 DEFINE_SIZE_MIN (8); 602}; 603 604struct ChainRuleSet 605{ 606 inline bool apply (hb_apply_context_t *c, ChainContextLookupContext &lookup_context) const 607 { 608 TRACE_APPLY (); 609 unsigned int num_rules = rule.len; 610 for (unsigned int i = 0; i < num_rules; i++) 611 { 612 if ((this+rule[i]).apply (c, lookup_context)) 613 return true; 614 } 615 616 return false; 617 } 618 619 inline bool sanitize (hb_sanitize_context_t *c) { 620 TRACE_SANITIZE (); 621 return rule.sanitize (c, this); 622 } 623 624 private: 625 OffsetArrayOf<ChainRule> 626 rule; /* Array of ChainRule tables 627 * ordered by preference */ 628 public: 629 DEFINE_SIZE_ARRAY (2, rule); 630}; 631 632struct ChainContextFormat1 633{ 634 friend struct ChainContext; 635 636 private: 637 inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const 638 { 639 TRACE_APPLY (); 640 unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint); 641 if (likely (index == NOT_COVERED)) 642 return false; 643 644 const ChainRuleSet &rule_set = this+ruleSet[index]; 645 struct ChainContextLookupContext lookup_context = { 646 {match_glyph, apply_func}, 647 {NULL, NULL, NULL} 648 }; 649 return rule_set.apply (c, lookup_context); 650 } 651 652 inline bool sanitize (hb_sanitize_context_t *c) { 653 TRACE_SANITIZE (); 654 return coverage.sanitize (c, this) 655 && ruleSet.sanitize (c, this); 656 } 657 658 private: 659 USHORT format; /* Format identifier--format = 1 */ 660 OffsetTo<Coverage> 661 coverage; /* Offset to Coverage table--from 662 * beginning of table */ 663 OffsetArrayOf<ChainRuleSet> 664 ruleSet; /* Array of ChainRuleSet tables 665 * ordered by Coverage Index */ 666 public: 667 DEFINE_SIZE_ARRAY (6, ruleSet); 668}; 669 670struct ChainContextFormat2 671{ 672 friend struct ChainContext; 673 674 private: 675 inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const 676 { 677 TRACE_APPLY (); 678 unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint); 679 if (likely (index == NOT_COVERED)) 680 return false; 681 682 const ClassDef &backtrack_class_def = this+backtrackClassDef; 683 const ClassDef &input_class_def = this+inputClassDef; 684 const ClassDef &lookahead_class_def = this+lookaheadClassDef; 685 686 index = input_class_def (c->buffer->info[c->buffer->i].codepoint); 687 const ChainRuleSet &rule_set = this+ruleSet[index]; 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 (c, lookup_context); 695 } 696 697 inline bool sanitize (hb_sanitize_context_t *c) { 698 TRACE_SANITIZE (); 699 return coverage.sanitize (c, this) 700 && backtrackClassDef.sanitize (c, this) 701 && inputClassDef.sanitize (c, this) 702 && lookaheadClassDef.sanitize (c, this) 703 && ruleSet.sanitize (c, 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_ARRAY (12, ruleSet); 728}; 729 730struct ChainContextFormat3 731{ 732 friend struct ChainContext; 733 734 private: 735 736 inline bool apply (hb_apply_context_t *c, 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]) (c->buffer->info[c->buffer->i].codepoint); 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 (c, 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 *c) { 761 TRACE_SANITIZE (); 762 if (!backtrack.sanitize (c, this)) return false; 763 OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack); 764 if (!input.sanitize (c, this)) return false; 765 OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input); 766 if (!lookahead.sanitize (c, this)) return false; 767 ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead); 768 return lookup.sanitize (c); 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 *c, apply_lookup_func_t apply_func) const 796 { 797 TRACE_APPLY (); 798 switch (u.format) { 799 case 1: return u.format1.apply (c, apply_func); 800 case 2: return u.format2.apply (c, apply_func); 801 case 3: return u.format3.apply (c, apply_func); 802 default:return false; 803 } 804 } 805 806 inline bool sanitize (hb_sanitize_context_t *c) { 807 TRACE_SANITIZE (); 808 if (!u.format.sanitize (c)) return false; 809 switch (u.format) { 810 case 1: return u.format1.sanitize (c); 811 case 2: return u.format2.sanitize (c); 812 case 3: return u.format3.sanitize (c); 813 default:return true; 814 } 815 } 816 817 private: 818 union { 819 USHORT format; /* Format identifier */ 820 ChainContextFormat1 format1; 821 ChainContextFormat2 format2; 822 ChainContextFormat3 format3; 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 *c) { 836 TRACE_SANITIZE (); 837 return c->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 *c) { 869 TRACE_SANITIZE (); 870 if (!u.format.sanitize (c)) return false; 871 switch (u.format) { 872 case 1: return u.format1.sanitize (c); 873 default:return true; 874 } 875 } 876 877 private: 878 union { 879 USHORT format; /* Format identifier */ 880 ExtensionFormat1 format1; 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 *c) { 926 TRACE_SANITIZE (); 927 return version.sanitize (c) && likely (version.major == 1) 928 && scriptList.sanitize (c, this) 929 && featureList.sanitize (c, this) 930 && lookupList.sanitize (c, 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 947HB_END_DECLS 948 949#endif /* HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH */ 950