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