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