hb-ot-layout-gsubgpos-private.hh revision 705e215268aa95c2bc6af8af9b48b72b690ec1f7
1/* 2 * Copyright (C) 2007,2008,2009 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 char *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 char *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 char *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 char *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 char *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 char *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 char *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 static inline unsigned int get_size () { return sizeof (LookupRecord); } 176 177 inline bool sanitize (hb_sanitize_context_t *context) { 178 TRACE_SANITIZE (); 179 return SANITIZE_SELF (); 180 } 181 182 USHORT sequenceIndex; /* Index into current glyph 183 * sequence--first glyph = 0 */ 184 USHORT lookupListIndex; /* Lookup to apply to that 185 * position--zero--based */ 186}; 187ASSERT_SIZE (LookupRecord, 4); 188 189static inline bool apply_lookup (hb_apply_context_t *context, 190 unsigned int count, /* Including the first glyph */ 191 unsigned int lookupCount, 192 const LookupRecord lookupRecord[], /* Array of LookupRecords--in design order */ 193 apply_lookup_func_t apply_func) 194{ 195 unsigned int end = MIN (context->buffer->in_length, context->buffer->in_pos + context->context_length); 196 if (unlikely (context->buffer->in_pos + count > end)) 197 return false; 198 199 /* TODO We don't support lookupRecord arrays that are not increasing: 200 * Should be easy for in_place ones at least. */ 201 202 /* Note: If sublookup is reverse, i will underflow after the first loop 203 * and we jump out of it. Not entirely disastrous. So we don't check 204 * for reverse lookup here. 205 */ 206 for (unsigned int i = 0; i < count; /* NOP */) 207 { 208 while (_hb_ot_layout_skip_mark (context->layout->face, IN_CURINFO (), context->lookup_flag, NULL)) 209 { 210 if (unlikely (context->buffer->in_pos == end)) 211 return true; 212 /* No lookup applied for this index */ 213 _hb_buffer_next_glyph (context->buffer); 214 } 215 216 if (lookupCount && i == lookupRecord->sequenceIndex) 217 { 218 unsigned int old_pos = context->buffer->in_pos; 219 220 /* Apply a lookup */ 221 bool done = apply_func (context, lookupRecord->lookupListIndex); 222 223 lookupRecord++; 224 lookupCount--; 225 /* Err, this is wrong if the lookup jumped over some glyphs */ 226 i += context->buffer->in_pos - old_pos; 227 if (unlikely (context->buffer->in_pos == end)) 228 return true; 229 230 if (!done) 231 goto not_applied; 232 } 233 else 234 { 235 not_applied: 236 /* No lookup applied for this index */ 237 _hb_buffer_next_glyph (context->buffer); 238 i++; 239 } 240 } 241 242 return true; 243} 244 245 246/* Contextual lookups */ 247 248struct ContextLookupContext 249{ 250 ContextFuncs funcs; 251 const char *match_data; 252}; 253 254static inline bool context_lookup (hb_apply_context_t *context, 255 unsigned int inputCount, /* Including the first glyph (not matched) */ 256 const USHORT input[], /* Array of input values--start with second glyph */ 257 unsigned int lookupCount, 258 const LookupRecord lookupRecord[], 259 ContextLookupContext &lookup_context) 260{ 261 hb_apply_context_t new_context = *context; 262 return match_input (context, 263 inputCount, input, 264 lookup_context.funcs.match, lookup_context.match_data, 265 &new_context.context_length) 266 && apply_lookup (&new_context, 267 inputCount, 268 lookupCount, lookupRecord, 269 lookup_context.funcs.apply); 270} 271 272struct Rule 273{ 274 friend struct RuleSet; 275 276 private: 277 inline bool apply (hb_apply_context_t *context, ContextLookupContext &lookup_context) const 278 { 279 TRACE_APPLY (); 280 const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].get_size () * (inputCount ? inputCount - 1 : 0)); 281 return context_lookup (context, 282 inputCount, input, 283 lookupCount, lookupRecord, 284 lookup_context); 285 } 286 287 public: 288 inline bool sanitize (hb_sanitize_context_t *context) { 289 TRACE_SANITIZE (); 290 if (!(SANITIZE (inputCount) && SANITIZE (lookupCount))) return false; 291 return SANITIZE_MEM (input, 292 input[0].get_size () * inputCount + 293 lookupRecordX[0].get_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}; 306ASSERT_SIZE_VAR2 (Rule, 4, USHORT, LookupRecord); 307 308struct RuleSet 309{ 310 inline bool apply (hb_apply_context_t *context, ContextLookupContext &lookup_context) const 311 { 312 TRACE_APPLY (); 313 unsigned int num_rules = rule.len; 314 for (unsigned int i = 0; i < num_rules; i++) 315 { 316 if ((this+rule[i]).apply (context, lookup_context)) 317 return true; 318 } 319 320 return false; 321 } 322 323 inline bool sanitize (hb_sanitize_context_t *context) { 324 TRACE_SANITIZE (); 325 return SANITIZE_WITH_BASE (this, rule); 326 } 327 328 private: 329 OffsetArrayOf<Rule> 330 rule; /* Array of Rule tables 331 * ordered by preference */ 332}; 333 334 335struct ContextFormat1 336{ 337 friend struct Context; 338 339 private: 340 inline bool apply (hb_apply_context_t *context, apply_lookup_func_t apply_func) const 341 { 342 TRACE_APPLY (); 343 unsigned int index = (this+coverage) (IN_CURGLYPH ()); 344 if (likely (index == NOT_COVERED)) 345 return false; 346 347 const RuleSet &rule_set = this+ruleSet[index]; 348 struct ContextLookupContext lookup_context = { 349 {match_glyph, apply_func}, 350 NULL 351 }; 352 return rule_set.apply (context, lookup_context); 353 } 354 355 inline bool sanitize (hb_sanitize_context_t *context) { 356 TRACE_SANITIZE (); 357 return SANITIZE_WITH_BASE (this, coverage) 358 && SANITIZE_WITH_BASE (this, ruleSet); 359 } 360 361 private: 362 USHORT format; /* Format identifier--format = 1 */ 363 OffsetTo<Coverage> 364 coverage; /* Offset to Coverage table--from 365 * beginning of table */ 366 OffsetArrayOf<RuleSet> 367 ruleSet; /* Array of RuleSet tables 368 * ordered by Coverage Index */ 369}; 370ASSERT_SIZE (ContextFormat1, 6); 371 372 373struct ContextFormat2 374{ 375 friend struct Context; 376 377 private: 378 inline bool apply (hb_apply_context_t *context, apply_lookup_func_t apply_func) const 379 { 380 TRACE_APPLY (); 381 unsigned int index = (this+coverage) (IN_CURGLYPH ()); 382 if (likely (index == NOT_COVERED)) 383 return false; 384 385 const ClassDef &class_def = this+classDef; 386 index = class_def (IN_CURGLYPH ()); 387 const RuleSet &rule_set = this+ruleSet[index]; 388 /* LONGTERMTODO: Old code fetches glyph classes at most once and caches 389 * them across subrule lookups. Not sure it's worth it. 390 */ 391 struct ContextLookupContext lookup_context = { 392 {match_class, apply_func}, 393 CharP(&class_def) 394 }; 395 return rule_set.apply (context, lookup_context); 396 } 397 398 inline bool sanitize (hb_sanitize_context_t *context) { 399 TRACE_SANITIZE (); 400 return SANITIZE_WITH_BASE (this, coverage) 401 && SANITIZE_WITH_BASE (this, classDef) 402 && SANITIZE_WITH_BASE (this, ruleSet); 403 } 404 405 private: 406 USHORT format; /* Format identifier--format = 2 */ 407 OffsetTo<Coverage> 408 coverage; /* Offset to Coverage table--from 409 * beginning of table */ 410 OffsetTo<ClassDef> 411 classDef; /* Offset to glyph ClassDef table--from 412 * beginning of table */ 413 OffsetArrayOf<RuleSet> 414 ruleSet; /* Array of RuleSet tables 415 * ordered by class */ 416}; 417ASSERT_SIZE (ContextFormat2, 8); 418 419 420struct ContextFormat3 421{ 422 friend struct Context; 423 424 private: 425 inline bool apply (hb_apply_context_t *context, apply_lookup_func_t apply_func) const 426 { 427 TRACE_APPLY (); 428 unsigned int index = (this+coverage[0]) (IN_CURGLYPH ()); 429 if (likely (index == NOT_COVERED)) 430 return false; 431 432 const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].get_size () * glyphCount); 433 struct ContextLookupContext lookup_context = { 434 {match_coverage, apply_func}, 435 CharP(this) 436 }; 437 return context_lookup (context, 438 glyphCount, (const USHORT *) (coverage + 1), 439 lookupCount, lookupRecord, 440 lookup_context); 441 } 442 443 inline bool sanitize (hb_sanitize_context_t *context) { 444 TRACE_SANITIZE (); 445 if (!SANITIZE_SELF ()) return false; 446 unsigned int count = glyphCount; 447 if (!SANITIZE_ARRAY (coverage, OffsetTo<Coverage>::get_size (), glyphCount)) return false; 448 for (unsigned int i = 0; i < count; i++) 449 if (!SANITIZE_WITH_BASE (this, coverage[i])) return false; 450 LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, OffsetTo<Coverage>::get_size () * glyphCount); 451 return SANITIZE_ARRAY (lookupRecord, LookupRecord::get_size (), lookupCount); 452 } 453 454 private: 455 USHORT format; /* Format identifier--format = 3 */ 456 USHORT glyphCount; /* Number of glyphs in the input glyph 457 * sequence */ 458 USHORT lookupCount; /* Number of LookupRecords */ 459 OffsetTo<Coverage> 460 coverage[VAR]; /* Array of offsets to Coverage 461 * table in glyph sequence order */ 462 LookupRecord lookupRecordX[VAR]; /* Array of LookupRecords--in 463 * design order */ 464}; 465ASSERT_SIZE_VAR2 (ContextFormat3, 6, OffsetTo<Coverage>, LookupRecord); 466 467struct Context 468{ 469 protected: 470 inline bool apply (hb_apply_context_t *context, apply_lookup_func_t apply_func) const 471 { 472 TRACE_APPLY (); 473 switch (u.format) { 474 case 1: return u.format1->apply (context, apply_func); 475 case 2: return u.format2->apply (context, apply_func); 476 case 3: return u.format3->apply (context, apply_func); 477 default:return false; 478 } 479 } 480 481 inline bool sanitize (hb_sanitize_context_t *context) { 482 TRACE_SANITIZE (); 483 if (!SANITIZE (u.format)) return false; 484 switch (u.format) { 485 case 1: return u.format1->sanitize (context); 486 case 2: return u.format2->sanitize (context); 487 case 3: return u.format3->sanitize (context); 488 default:return true; 489 } 490 } 491 492 private: 493 union { 494 USHORT format; /* Format identifier */ 495 ContextFormat1 format1[VAR]; 496 ContextFormat2 format2[VAR]; 497 ContextFormat3 format3[VAR]; 498 } u; 499}; 500 501 502/* Chaining Contextual lookups */ 503 504struct ChainContextLookupContext 505{ 506 ContextFuncs funcs; 507 const char *match_data[3]; 508}; 509 510static inline bool chain_context_lookup (hb_apply_context_t *context, 511 unsigned int backtrackCount, 512 const USHORT backtrack[], 513 unsigned int inputCount, /* Including the first glyph (not matched) */ 514 const USHORT input[], /* Array of input values--start with second glyph */ 515 unsigned int lookaheadCount, 516 const USHORT lookahead[], 517 unsigned int lookupCount, 518 const LookupRecord lookupRecord[], 519 ChainContextLookupContext &lookup_context) 520{ 521 /* First guess */ 522 if (unlikely (context->buffer->out_pos < backtrackCount || 523 context->buffer->in_pos + inputCount + lookaheadCount > context->buffer->in_length || 524 inputCount + lookaheadCount > context->context_length)) 525 return false; 526 527 hb_apply_context_t new_context = *context; 528 return match_backtrack (context, 529 backtrackCount, backtrack, 530 lookup_context.funcs.match, lookup_context.match_data[0]) 531 && match_input (context, 532 inputCount, input, 533 lookup_context.funcs.match, lookup_context.match_data[1], 534 &new_context.context_length) 535 && match_lookahead (context, 536 lookaheadCount, lookahead, 537 lookup_context.funcs.match, lookup_context.match_data[2], 538 new_context.context_length) 539 && apply_lookup (&new_context, 540 inputCount, 541 lookupCount, lookupRecord, 542 lookup_context.funcs.apply); 543} 544 545struct ChainRule 546{ 547 friend struct ChainRuleSet; 548 549 private: 550 inline bool apply (hb_apply_context_t *context, ChainContextLookupContext &lookup_context) const 551 { 552 TRACE_APPLY (); 553 const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack); 554 const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input); 555 const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead); 556 return chain_context_lookup (context, 557 backtrack.len, backtrack.array(), 558 input.len, input.array(), 559 lookahead.len, lookahead.array(), 560 lookup.len, lookup.array(), 561 lookup_context); 562 return false; 563 } 564 565 public: 566 inline bool sanitize (hb_sanitize_context_t *context) { 567 TRACE_SANITIZE (); 568 if (!SANITIZE (backtrack)) return false; 569 HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack); 570 if (!SANITIZE (input)) return false; 571 ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input); 572 if (!SANITIZE (lookahead)) return false; 573 ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead); 574 return SANITIZE (lookup); 575 } 576 577 private: 578 ArrayOf<USHORT> 579 backtrack; /* Array of backtracking values 580 * (to be matched before the input 581 * sequence) */ 582 HeadlessArrayOf<USHORT> 583 inputX; /* Array of input values (start with 584 * second glyph) */ 585 ArrayOf<USHORT> 586 lookaheadX; /* Array of lookahead values's (to be 587 * matched after the input sequence) */ 588 ArrayOf<LookupRecord> 589 lookupX; /* Array of LookupRecords--in 590 * design order) */ 591}; 592ASSERT_SIZE (ChainRule, 8); 593 594struct ChainRuleSet 595{ 596 inline bool apply (hb_apply_context_t *context, ChainContextLookupContext &lookup_context) const 597 { 598 TRACE_APPLY (); 599 unsigned int num_rules = rule.len; 600 for (unsigned int i = 0; i < num_rules; i++) 601 { 602 if ((this+rule[i]).apply (context, lookup_context)) 603 return true; 604 } 605 606 return false; 607 } 608 609 inline bool sanitize (hb_sanitize_context_t *context) { 610 TRACE_SANITIZE (); 611 return SANITIZE_WITH_BASE (this, rule); 612 } 613 614 private: 615 OffsetArrayOf<ChainRule> 616 rule; /* Array of ChainRule tables 617 * ordered by preference */ 618}; 619ASSERT_SIZE (ChainRuleSet, 2); 620 621struct ChainContextFormat1 622{ 623 friend struct ChainContext; 624 625 private: 626 inline bool apply (hb_apply_context_t *context, apply_lookup_func_t apply_func) const 627 { 628 TRACE_APPLY (); 629 unsigned int index = (this+coverage) (IN_CURGLYPH ()); 630 if (likely (index == NOT_COVERED)) 631 return false; 632 633 const ChainRuleSet &rule_set = this+ruleSet[index]; 634 struct ChainContextLookupContext lookup_context = { 635 {match_glyph, apply_func}, 636 {NULL, NULL, NULL} 637 }; 638 return rule_set.apply (context, lookup_context); 639 } 640 641 inline bool sanitize (hb_sanitize_context_t *context) { 642 TRACE_SANITIZE (); 643 return SANITIZE_WITH_BASE (this, coverage) 644 && SANITIZE_WITH_BASE (this, ruleSet); 645 } 646 647 private: 648 USHORT format; /* Format identifier--format = 1 */ 649 OffsetTo<Coverage> 650 coverage; /* Offset to Coverage table--from 651 * beginning of table */ 652 OffsetArrayOf<ChainRuleSet> 653 ruleSet; /* Array of ChainRuleSet tables 654 * ordered by Coverage Index */ 655}; 656ASSERT_SIZE (ChainContextFormat1, 6); 657 658struct ChainContextFormat2 659{ 660 friend struct ChainContext; 661 662 private: 663 inline bool apply (hb_apply_context_t *context, apply_lookup_func_t apply_func) const 664 { 665 TRACE_APPLY (); 666 unsigned int index = (this+coverage) (IN_CURGLYPH ()); 667 if (likely (index == NOT_COVERED)) 668 return false; 669 670 const ClassDef &backtrack_class_def = this+backtrackClassDef; 671 const ClassDef &input_class_def = this+inputClassDef; 672 const ClassDef &lookahead_class_def = this+lookaheadClassDef; 673 674 index = input_class_def (IN_CURGLYPH ()); 675 const ChainRuleSet &rule_set = this+ruleSet[index]; 676 /* LONGTERMTODO: Old code fetches glyph classes at most once and caches 677 * them across subrule lookups. Not sure it's worth it. 678 */ 679 struct ChainContextLookupContext lookup_context = { 680 {match_class, apply_func}, 681 {CharP(&backtrack_class_def), 682 CharP(&input_class_def), 683 CharP(&lookahead_class_def)} 684 }; 685 return rule_set.apply (context, lookup_context); 686 } 687 688 inline bool sanitize (hb_sanitize_context_t *context) { 689 TRACE_SANITIZE (); 690 return SANITIZE_WITH_BASE (this, coverage) 691 && SANITIZE_WITH_BASE (this, backtrackClassDef) 692 && SANITIZE_WITH_BASE (this, inputClassDef) 693 && SANITIZE_WITH_BASE (this, lookaheadClassDef) 694 && SANITIZE_WITH_BASE (this, ruleSet); 695 } 696 697 private: 698 USHORT format; /* Format identifier--format = 2 */ 699 OffsetTo<Coverage> 700 coverage; /* Offset to Coverage table--from 701 * beginning of table */ 702 OffsetTo<ClassDef> 703 backtrackClassDef; /* Offset to glyph ClassDef table 704 * containing backtrack sequence 705 * data--from beginning of table */ 706 OffsetTo<ClassDef> 707 inputClassDef; /* Offset to glyph ClassDef 708 * table containing input sequence 709 * data--from beginning of table */ 710 OffsetTo<ClassDef> 711 lookaheadClassDef; /* Offset to glyph ClassDef table 712 * containing lookahead sequence 713 * data--from beginning of table */ 714 OffsetArrayOf<ChainRuleSet> 715 ruleSet; /* Array of ChainRuleSet tables 716 * ordered by class */ 717}; 718ASSERT_SIZE (ChainContextFormat2, 12); 719 720struct ChainContextFormat3 721{ 722 friend struct ChainContext; 723 724 private: 725 726 inline bool apply (hb_apply_context_t *context, apply_lookup_func_t apply_func) const 727 { 728 TRACE_APPLY (); 729 const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack); 730 731 unsigned int index = (this+input[0]) (IN_CURGLYPH ()); 732 if (likely (index == NOT_COVERED)) 733 return false; 734 735 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input); 736 const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead); 737 struct ChainContextLookupContext lookup_context = { 738 {match_coverage, apply_func}, 739 {CharP(this), CharP(this), CharP(this)} 740 }; 741 return chain_context_lookup (context, 742 backtrack.len, (const USHORT *) backtrack.array(), 743 input.len, (const USHORT *) input.array() + 1, 744 lookahead.len, (const USHORT *) lookahead.array(), 745 lookup.len, lookup.array(), 746 lookup_context); 747 return false; 748 } 749 750 inline bool sanitize (hb_sanitize_context_t *context) { 751 TRACE_SANITIZE (); 752 if (!SANITIZE_WITH_BASE (this, backtrack)) return false; 753 OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack); 754 if (!SANITIZE_WITH_BASE (this, input)) return false; 755 OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input); 756 if (!SANITIZE_WITH_BASE (this, lookahead)) return false; 757 ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead); 758 return SANITIZE (lookup); 759 } 760 761 private: 762 USHORT format; /* Format identifier--format = 3 */ 763 OffsetArrayOf<Coverage> 764 backtrack; /* Array of coverage tables 765 * in backtracking sequence, in glyph 766 * sequence order */ 767 OffsetArrayOf<Coverage> 768 inputX ; /* Array of coverage 769 * tables in input sequence, in glyph 770 * sequence order */ 771 OffsetArrayOf<Coverage> 772 lookaheadX; /* Array of coverage tables 773 * in lookahead sequence, in glyph 774 * sequence order */ 775 ArrayOf<LookupRecord> 776 lookupX; /* Array of LookupRecords--in 777 * design order) */ 778}; 779ASSERT_SIZE (ChainContextFormat3, 10); 780 781struct ChainContext 782{ 783 protected: 784 inline bool apply (hb_apply_context_t *context, apply_lookup_func_t apply_func) const 785 { 786 TRACE_APPLY (); 787 switch (u.format) { 788 case 1: return u.format1->apply (context, apply_func); 789 case 2: return u.format2->apply (context, apply_func); 790 case 3: return u.format3->apply (context, apply_func); 791 default:return false; 792 } 793 } 794 795 inline bool sanitize (hb_sanitize_context_t *context) { 796 TRACE_SANITIZE (); 797 if (!SANITIZE (u.format)) return false; 798 switch (u.format) { 799 case 1: return u.format1->sanitize (context); 800 case 2: return u.format2->sanitize (context); 801 case 3: return u.format3->sanitize (context); 802 default:return true; 803 } 804 } 805 806 private: 807 union { 808 USHORT format; /* Format identifier */ 809 ChainContextFormat1 format1[VAR]; 810 ChainContextFormat2 format2[VAR]; 811 ChainContextFormat3 format3[VAR]; 812 } u; 813}; 814 815 816struct ExtensionFormat1 817{ 818 friend struct Extension; 819 820 protected: 821 inline unsigned int get_type (void) const { return extensionLookupType; } 822 inline unsigned int get_offset (void) const { return extensionOffset; } 823 824 inline bool sanitize (hb_sanitize_context_t *context) { 825 TRACE_SANITIZE (); 826 return SANITIZE_SELF (); 827 } 828 829 private: 830 USHORT format; /* Format identifier. Set to 1. */ 831 USHORT extensionLookupType; /* Lookup type of subtable referenced 832 * by ExtensionOffset (i.e. the 833 * extension subtable). */ 834 ULONG extensionOffset; /* Offset to the extension subtable, 835 * of lookup type subtable. */ 836}; 837ASSERT_SIZE (ExtensionFormat1, 8); 838 839struct Extension 840{ 841 inline unsigned int get_type (void) const 842 { 843 switch (u.format) { 844 case 1: return u.format1->get_type (); 845 default:return 0; 846 } 847 } 848 inline unsigned int get_offset (void) const 849 { 850 switch (u.format) { 851 case 1: return u.format1->get_offset (); 852 default:return 0; 853 } 854 } 855 856 inline bool sanitize (hb_sanitize_context_t *context) { 857 TRACE_SANITIZE (); 858 if (!SANITIZE (u.format)) return false; 859 switch (u.format) { 860 case 1: return u.format1->sanitize (context); 861 default:return true; 862 } 863 } 864 865 private: 866 union { 867 USHORT format; /* Format identifier */ 868 ExtensionFormat1 format1[VAR]; 869 } u; 870}; 871 872 873/* 874 * GSUB/GPOS Common 875 */ 876 877struct GSUBGPOS 878{ 879 static const hb_tag_t GSUBTag = HB_OT_TAG_GSUB; 880 static const hb_tag_t GPOSTag = HB_OT_TAG_GPOS; 881 882 inline unsigned int get_script_count (void) const 883 { return (this+scriptList).len; } 884 inline const Tag& get_script_tag (unsigned int i) const 885 { return (this+scriptList).get_tag (i); } 886 inline unsigned int get_script_tags (unsigned int start_offset, 887 unsigned int *script_count /* IN/OUT */, 888 hb_tag_t *script_tags /* OUT */) const 889 { return (this+scriptList).get_tags (start_offset, script_count, script_tags); } 890 inline const Script& get_script (unsigned int i) const 891 { return (this+scriptList)[i]; } 892 inline bool find_script_index (hb_tag_t tag, unsigned int *index) const 893 { return (this+scriptList).find_index (tag, index); } 894 895 inline unsigned int get_feature_count (void) const 896 { return (this+featureList).len; } 897 inline const Tag& get_feature_tag (unsigned int i) const 898 { return (this+featureList).get_tag (i); } 899 inline unsigned int get_feature_tags (unsigned int start_offset, 900 unsigned int *feature_count /* IN/OUT */, 901 hb_tag_t *feature_tags /* OUT */) const 902 { return (this+featureList).get_tags (start_offset, feature_count, feature_tags); } 903 inline const Feature& get_feature (unsigned int i) const 904 { return (this+featureList)[i]; } 905 inline bool find_feature_index (hb_tag_t tag, unsigned int *index) const 906 { return (this+featureList).find_index (tag, index); } 907 908 inline unsigned int get_lookup_count (void) const 909 { return (this+lookupList).len; } 910 inline const Lookup& get_lookup (unsigned int i) const 911 { return (this+lookupList)[i]; } 912 913 inline bool sanitize (hb_sanitize_context_t *context) { 914 TRACE_SANITIZE (); 915 return SANITIZE (version) && likely (version.major == 1) 916 && SANITIZE_WITH_BASE (this, scriptList) 917 && SANITIZE_WITH_BASE (this, featureList) 918 && SANITIZE_WITH_BASE (this, lookupList); 919 } 920 921 protected: 922 FixedVersion version; /* Version of the GSUB/GPOS table--initially set 923 * to 0x00010000 */ 924 OffsetTo<ScriptList> 925 scriptList; /* ScriptList table */ 926 OffsetTo<FeatureList> 927 featureList; /* FeatureList table */ 928 OffsetTo<LookupList> 929 lookupList; /* LookupList table */ 930}; 931ASSERT_SIZE (GSUBGPOS, 10); 932 933 934#endif /* HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH */ 935