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