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