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