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