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