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