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