hb-aat-layout-morx-table.hh (45705B)
1 /* 2 * Copyright © 2017 Google, Inc. 3 * 4 * This is part of HarfBuzz, a text shaping 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 * Google Author(s): Behdad Esfahbod 25 */ 26 27 #ifndef HB_AAT_LAYOUT_MORX_TABLE_HH 28 #define HB_AAT_LAYOUT_MORX_TABLE_HH 29 30 #include "hb-open-type.hh" 31 #include "hb-aat-layout-common.hh" 32 #include "hb-ot-layout.hh" 33 #include "hb-aat-map.hh" 34 35 /* 36 * morx -- Extended Glyph Metamorphosis 37 * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html 38 * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6mort.html 39 */ 40 #define HB_AAT_TAG_morx HB_TAG('m','o','r','x') 41 #define HB_AAT_TAG_mort HB_TAG('m','o','r','t') 42 43 44 namespace AAT { 45 46 using namespace OT; 47 48 template <typename Types> 49 struct RearrangementSubtable 50 { 51 typedef typename Types::HBUINT HBUINT; 52 53 typedef void EntryData; 54 55 enum Flags 56 { 57 MarkFirst = 0x8000, /* If set, make the current glyph the first 58 * glyph to be rearranged. */ 59 DontAdvance = 0x4000, /* If set, don't advance to the next glyph 60 * before going to the new state. This means 61 * that the glyph index doesn't change, even 62 * if the glyph at that index has changed. */ 63 MarkLast = 0x2000, /* If set, make the current glyph the last 64 * glyph to be rearranged. */ 65 Reserved = 0x1FF0, /* These bits are reserved and should be set to 0. */ 66 Verb = 0x000F, /* The type of rearrangement specified. */ 67 }; 68 69 bool is_action_initiable (const Entry<EntryData> &entry) const 70 { 71 return (entry.flags & MarkFirst); 72 } 73 bool is_actionable (const Entry<EntryData> &entry) const 74 { 75 return (entry.flags & Verb); 76 } 77 78 struct driver_context_t 79 { 80 static constexpr bool in_place = true; 81 82 driver_context_t (const RearrangementSubtable *table_) : 83 ret (false), 84 table (table_), 85 start (0), end (0) {} 86 87 void transition (hb_buffer_t *buffer, 88 StateTableDriver<Types, EntryData, Flags> *driver, 89 const Entry<EntryData> &entry) 90 { 91 unsigned int flags = entry.flags; 92 93 if (flags & MarkFirst) 94 start = buffer->idx; 95 96 if (flags & MarkLast) 97 end = hb_min (buffer->idx + 1, buffer->len); 98 99 if ((flags & Verb) && start < end) 100 { 101 /* The following map has two nibbles, for start-side 102 * and end-side. Values of 0,1,2 mean move that many 103 * to the other side. Value of 3 means move 2 and 104 * flip them. */ 105 const unsigned char map[16] = 106 { 107 0x00, /* 0 no change */ 108 0x10, /* 1 Ax => xA */ 109 0x01, /* 2 xD => Dx */ 110 0x11, /* 3 AxD => DxA */ 111 0x20, /* 4 ABx => xAB */ 112 0x30, /* 5 ABx => xBA */ 113 0x02, /* 6 xCD => CDx */ 114 0x03, /* 7 xCD => DCx */ 115 0x12, /* 8 AxCD => CDxA */ 116 0x13, /* 9 AxCD => DCxA */ 117 0x21, /* 10 ABxD => DxAB */ 118 0x31, /* 11 ABxD => DxBA */ 119 0x22, /* 12 ABxCD => CDxAB */ 120 0x32, /* 13 ABxCD => CDxBA */ 121 0x23, /* 14 ABxCD => DCxAB */ 122 0x33, /* 15 ABxCD => DCxBA */ 123 }; 124 125 unsigned int m = map[flags & Verb]; 126 unsigned int l = hb_min (2u, m >> 4); 127 unsigned int r = hb_min (2u, m & 0x0F); 128 bool reverse_l = 3 == (m >> 4); 129 bool reverse_r = 3 == (m & 0x0F); 130 131 if (end - start >= l + r && end-start <= HB_MAX_CONTEXT_LENGTH) 132 { 133 buffer->merge_clusters (start, hb_min (buffer->idx + 1, buffer->len)); 134 buffer->merge_clusters (start, end); 135 136 hb_glyph_info_t *info = buffer->info; 137 hb_glyph_info_t buf[4]; 138 139 hb_memcpy (buf, info + start, l * sizeof (buf[0])); 140 hb_memcpy (buf + 2, info + end - r, r * sizeof (buf[0])); 141 142 if (l != r) 143 memmove (info + start + r, info + start + l, (end - start - l - r) * sizeof (buf[0])); 144 145 hb_memcpy (info + start, buf + 2, r * sizeof (buf[0])); 146 hb_memcpy (info + end - l, buf, l * sizeof (buf[0])); 147 if (reverse_l) 148 { 149 buf[0] = info[end - 1]; 150 info[end - 1] = info[end - 2]; 151 info[end - 2] = buf[0]; 152 } 153 if (reverse_r) 154 { 155 buf[0] = info[start]; 156 info[start] = info[start + 1]; 157 info[start + 1] = buf[0]; 158 } 159 } 160 } 161 } 162 163 public: 164 bool ret; 165 const RearrangementSubtable *table; 166 private: 167 unsigned int start; 168 unsigned int end; 169 }; 170 171 bool apply (hb_aat_apply_context_t *c) const 172 { 173 TRACE_APPLY (this); 174 175 driver_context_t dc (this); 176 177 StateTableDriver<Types, EntryData, Flags> driver (machine, c->face); 178 179 driver.drive (&dc, c); 180 181 return_trace (dc.ret); 182 } 183 184 bool sanitize (hb_sanitize_context_t *c) const 185 { 186 TRACE_SANITIZE (this); 187 return_trace (machine.sanitize (c)); 188 } 189 190 public: 191 StateTable<Types, EntryData> machine; 192 public: 193 DEFINE_SIZE_STATIC ((StateTable<Types, EntryData>::static_size)); 194 }; 195 196 template <typename Types> 197 struct ContextualSubtable 198 { 199 typedef typename Types::HBUINT HBUINT; 200 201 struct EntryData 202 { 203 HBUINT16 markIndex; /* Index of the substitution table for the 204 * marked glyph (use 0xFFFF for none). */ 205 HBUINT16 currentIndex; /* Index of the substitution table for the 206 * current glyph (use 0xFFFF for none). */ 207 public: 208 DEFINE_SIZE_STATIC (4); 209 }; 210 211 enum Flags 212 { 213 SetMark = 0x8000, /* If set, make the current glyph the marked glyph. */ 214 DontAdvance = 0x4000, /* If set, don't advance to the next glyph before 215 * going to the new state. */ 216 Reserved = 0x3FFF, /* These bits are reserved and should be set to 0. */ 217 }; 218 219 bool is_action_initiable (const Entry<EntryData> &entry) const 220 { 221 return (entry.flags & SetMark); 222 } 223 bool is_actionable (const Entry<EntryData> &entry) const 224 { 225 return entry.data.markIndex != 0xFFFF || entry.data.currentIndex != 0xFFFF; 226 } 227 228 struct driver_context_t 229 { 230 static constexpr bool in_place = true; 231 232 driver_context_t (const ContextualSubtable *table_, 233 hb_aat_apply_context_t *c_) : 234 ret (false), 235 c (c_), 236 table (table_), 237 mark_set (false), 238 mark (0), 239 subs (table+table->substitutionTables) {} 240 241 void transition (hb_buffer_t *buffer, 242 StateTableDriver<Types, EntryData, Flags> *driver, 243 const Entry<EntryData> &entry) 244 { 245 /* Looks like CoreText applies neither mark nor current substitution for 246 * end-of-text if mark was not explicitly set. */ 247 if (buffer->idx == buffer->len && !mark_set) 248 return; 249 250 const HBGlyphID16 *replacement; 251 252 replacement = nullptr; 253 if (Types::extended) 254 { 255 if (entry.data.markIndex != 0xFFFF) 256 { 257 const Lookup<HBGlyphID16> &lookup = subs[entry.data.markIndex]; 258 replacement = lookup.get_value (buffer->info[mark].codepoint, driver->num_glyphs); 259 } 260 } 261 else 262 { 263 unsigned int offset = entry.data.markIndex + buffer->info[mark].codepoint; 264 const UnsizedArrayOf<HBGlyphID16> &subs_old = (const UnsizedArrayOf<HBGlyphID16> &) subs; 265 replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)]; 266 if (!(replacement->sanitize (&c->sanitizer) && 267 hb_barrier () && 268 *replacement)) 269 replacement = nullptr; 270 } 271 if (replacement) 272 { 273 buffer->unsafe_to_break (mark, hb_min (buffer->idx + 1, buffer->len)); 274 c->replace_glyph_inplace (mark, *replacement); 275 ret = true; 276 } 277 278 replacement = nullptr; 279 unsigned int idx = hb_min (buffer->idx, buffer->len - 1); 280 if (Types::extended) 281 { 282 if (entry.data.currentIndex != 0xFFFF) 283 { 284 const Lookup<HBGlyphID16> &lookup = subs[entry.data.currentIndex]; 285 replacement = lookup.get_value (buffer->info[idx].codepoint, driver->num_glyphs); 286 } 287 } 288 else 289 { 290 unsigned int offset = entry.data.currentIndex + buffer->info[idx].codepoint; 291 const UnsizedArrayOf<HBGlyphID16> &subs_old = (const UnsizedArrayOf<HBGlyphID16> &) subs; 292 replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)]; 293 if (!(replacement->sanitize (&c->sanitizer) && 294 hb_barrier () && 295 *replacement)) 296 replacement = nullptr; 297 } 298 if (replacement) 299 { 300 c->replace_glyph_inplace (idx, *replacement); 301 ret = true; 302 } 303 304 if (entry.flags & SetMark) 305 { 306 mark_set = true; 307 mark = buffer->idx; 308 } 309 } 310 311 public: 312 bool ret; 313 hb_aat_apply_context_t *c; 314 const ContextualSubtable *table; 315 private: 316 bool mark_set; 317 unsigned int mark; 318 const UnsizedListOfOffset16To<Lookup<HBGlyphID16>, HBUINT, void, false> &subs; 319 }; 320 321 bool apply (hb_aat_apply_context_t *c) const 322 { 323 TRACE_APPLY (this); 324 325 driver_context_t dc (this, c); 326 327 StateTableDriver<Types, EntryData, Flags> driver (machine, c->face); 328 329 driver.drive (&dc, c); 330 331 return_trace (dc.ret); 332 } 333 334 bool sanitize (hb_sanitize_context_t *c) const 335 { 336 TRACE_SANITIZE (this); 337 338 unsigned int num_entries = 0; 339 if (unlikely (!machine.sanitize (c, &num_entries))) return_trace (false); 340 hb_barrier (); 341 342 if (!Types::extended) 343 return_trace (substitutionTables.sanitize (c, this, 0)); 344 345 unsigned int num_lookups = 0; 346 347 const Entry<EntryData> *entries = machine.get_entries (); 348 for (unsigned int i = 0; i < num_entries; i++) 349 { 350 const EntryData &data = entries[i].data; 351 352 if (data.markIndex != 0xFFFF) 353 num_lookups = hb_max (num_lookups, 1u + data.markIndex); 354 if (data.currentIndex != 0xFFFF) 355 num_lookups = hb_max (num_lookups, 1u + data.currentIndex); 356 } 357 358 return_trace (substitutionTables.sanitize (c, this, num_lookups)); 359 } 360 361 public: 362 StateTable<Types, EntryData> 363 machine; 364 protected: 365 NNOffsetTo<UnsizedListOfOffset16To<Lookup<HBGlyphID16>, HBUINT, void, false>, HBUINT> 366 substitutionTables; 367 public: 368 DEFINE_SIZE_STATIC ((StateTable<Types, EntryData>::static_size + HBUINT::static_size)); 369 }; 370 371 372 template <bool extended> 373 struct LigatureEntry; 374 375 template <> 376 struct LigatureEntry<true> 377 { 378 379 struct EntryData 380 { 381 HBUINT16 ligActionIndex; /* Index to the first ligActionTable entry 382 * for processing this group, if indicated 383 * by the flags. */ 384 public: 385 DEFINE_SIZE_STATIC (2); 386 }; 387 388 enum Flags 389 { 390 SetComponent = 0x8000, /* Push this glyph onto the component stack for 391 * eventual processing. */ 392 DontAdvance = 0x4000, /* Leave the glyph pointer at this glyph for the 393 next iteration. */ 394 PerformAction = 0x2000, /* Use the ligActionIndex to process a ligature 395 * group. */ 396 Reserved = 0x1FFF, /* These bits are reserved and should be set to 0. */ 397 }; 398 399 static bool initiateAction (const Entry<EntryData> &entry) 400 { return entry.flags & SetComponent; } 401 402 static bool performAction (const Entry<EntryData> &entry) 403 { return entry.flags & PerformAction; } 404 405 static unsigned int ligActionIndex (const Entry<EntryData> &entry) 406 { return entry.data.ligActionIndex; } 407 }; 408 template <> 409 struct LigatureEntry<false> 410 { 411 typedef void EntryData; 412 413 enum Flags 414 { 415 SetComponent = 0x8000, /* Push this glyph onto the component stack for 416 * eventual processing. */ 417 DontAdvance = 0x4000, /* Leave the glyph pointer at this glyph for the 418 next iteration. */ 419 Offset = 0x3FFF, /* Byte offset from beginning of subtable to the 420 * ligature action list. This value must be a 421 * multiple of 4. */ 422 }; 423 424 static bool initiateAction (const Entry<EntryData> &entry) 425 { return entry.flags & SetComponent; } 426 427 static bool performAction (const Entry<EntryData> &entry) 428 { return entry.flags & Offset; } 429 430 static unsigned int ligActionIndex (const Entry<EntryData> &entry) 431 { return entry.flags & Offset; } 432 }; 433 434 435 template <typename Types> 436 struct LigatureSubtable 437 { 438 typedef typename Types::HBUINT HBUINT; 439 440 typedef LigatureEntry<Types::extended> LigatureEntryT; 441 typedef typename LigatureEntryT::EntryData EntryData; 442 443 enum Flags 444 { 445 DontAdvance = LigatureEntryT::DontAdvance, 446 }; 447 448 bool is_action_initiable (const Entry<EntryData> &entry) const 449 { 450 return LigatureEntryT::initiateAction (entry); 451 } 452 bool is_actionable (const Entry<EntryData> &entry) const 453 { 454 return LigatureEntryT::performAction (entry); 455 } 456 457 struct driver_context_t 458 { 459 static constexpr bool in_place = false; 460 enum LigActionFlags 461 { 462 LigActionLast = 0x80000000, /* This is the last action in the list. This also 463 * implies storage. */ 464 LigActionStore = 0x40000000, /* Store the ligature at the current cumulated index 465 * in the ligature table in place of the marked 466 * (i.e. currently-popped) glyph. */ 467 LigActionOffset = 0x3FFFFFFF, /* A 30-bit value which is sign-extended to 32-bits 468 * and added to the glyph ID, resulting in an index 469 * into the component table. */ 470 }; 471 472 driver_context_t (const LigatureSubtable *table_, 473 hb_aat_apply_context_t *c_) : 474 ret (false), 475 c (c_), 476 table (table_), 477 ligAction (table+table->ligAction), 478 component (table+table->component), 479 ligature (table+table->ligature), 480 match_length (0) {} 481 482 void transition (hb_buffer_t *buffer, 483 StateTableDriver<Types, EntryData, Flags> *driver, 484 const Entry<EntryData> &entry) 485 { 486 DEBUG_MSG (APPLY, nullptr, "Ligature transition at %u", buffer->idx); 487 if (entry.flags & LigatureEntryT::SetComponent) 488 { 489 /* Never mark same index twice, in case DontAdvance was used... */ 490 if (unlikely (match_length && match_positions[(match_length - 1u) % ARRAY_LENGTH (match_positions)] == buffer->out_len)) 491 match_length--; 492 493 match_positions[match_length++ % ARRAY_LENGTH (match_positions)] = buffer->out_len; 494 DEBUG_MSG (APPLY, nullptr, "Set component at %u", buffer->out_len); 495 } 496 497 if (LigatureEntryT::performAction (entry)) 498 { 499 DEBUG_MSG (APPLY, nullptr, "Perform action with %u", match_length); 500 unsigned int end = buffer->out_len; 501 502 if (unlikely (!match_length)) 503 return; 504 505 if (buffer->idx >= buffer->len) 506 return; /* TODO Work on previous instead? */ 507 508 unsigned int cursor = match_length; 509 510 unsigned int action_idx = LigatureEntryT::ligActionIndex (entry); 511 action_idx = Types::offsetToIndex (action_idx, table, ligAction.arrayZ); 512 const HBUINT32 *actionData = &ligAction[action_idx]; 513 514 unsigned int ligature_idx = 0; 515 unsigned int action; 516 do 517 { 518 if (unlikely (!cursor)) 519 { 520 /* Stack underflow. Clear the stack. */ 521 DEBUG_MSG (APPLY, nullptr, "Stack underflow"); 522 match_length = 0; 523 break; 524 } 525 526 DEBUG_MSG (APPLY, nullptr, "Moving to stack position %u", cursor - 1); 527 if (unlikely (!buffer->move_to (match_positions[--cursor % ARRAY_LENGTH (match_positions)]))) return; 528 529 if (unlikely (!actionData->sanitize (&c->sanitizer))) break; 530 hb_barrier (); 531 action = *actionData; 532 533 uint32_t uoffset = action & LigActionOffset; 534 if (uoffset & 0x20000000) 535 uoffset |= 0xC0000000; /* Sign-extend. */ 536 int32_t offset = (int32_t) uoffset; 537 unsigned int component_idx = buffer->cur().codepoint + offset; 538 component_idx = Types::wordOffsetToIndex (component_idx, table, component.arrayZ); 539 const HBUINT16 &componentData = component[component_idx]; 540 if (unlikely (!componentData.sanitize (&c->sanitizer))) break; 541 hb_barrier (); 542 ligature_idx += componentData; 543 544 DEBUG_MSG (APPLY, nullptr, "Action store %d last %d", 545 bool (action & LigActionStore), 546 bool (action & LigActionLast)); 547 if (action & (LigActionStore | LigActionLast)) 548 { 549 ligature_idx = Types::offsetToIndex (ligature_idx, table, ligature.arrayZ); 550 const HBGlyphID16 &ligatureData = ligature[ligature_idx]; 551 if (unlikely (!ligatureData.sanitize (&c->sanitizer))) break; 552 hb_barrier (); 553 hb_codepoint_t lig = ligatureData; 554 555 DEBUG_MSG (APPLY, nullptr, "Produced ligature %u", lig); 556 if (unlikely (!c->replace_glyph (lig))) return; 557 558 unsigned int lig_end = match_positions[(match_length - 1u) % ARRAY_LENGTH (match_positions)] + 1u; 559 /* Now go and delete all subsequent components. */ 560 while (match_length - 1u > cursor) 561 { 562 DEBUG_MSG (APPLY, nullptr, "Skipping ligature component"); 563 if (unlikely (!buffer->move_to (match_positions[--match_length % ARRAY_LENGTH (match_positions)]))) return; 564 if (!c->delete_glyph ()) return; 565 } 566 567 if (unlikely (!buffer->move_to (lig_end))) return; 568 buffer->merge_out_clusters (match_positions[cursor % ARRAY_LENGTH (match_positions)], buffer->out_len); 569 } 570 571 actionData++; 572 } 573 while (!(action & LigActionLast)); 574 if (unlikely (!buffer->move_to (end))) return; 575 } 576 } 577 578 public: 579 bool ret; 580 hb_aat_apply_context_t *c; 581 const LigatureSubtable *table; 582 private: 583 const UnsizedArrayOf<HBUINT32> &ligAction; 584 const UnsizedArrayOf<HBUINT16> &component; 585 const UnsizedArrayOf<HBGlyphID16> &ligature; 586 unsigned int match_length; 587 unsigned int match_positions[HB_MAX_CONTEXT_LENGTH]; 588 }; 589 590 bool apply (hb_aat_apply_context_t *c) const 591 { 592 TRACE_APPLY (this); 593 594 driver_context_t dc (this, c); 595 596 StateTableDriver<Types, EntryData, Flags> driver (machine, c->face); 597 598 driver.drive (&dc, c); 599 600 return_trace (dc.ret); 601 } 602 603 bool sanitize (hb_sanitize_context_t *c) const 604 { 605 TRACE_SANITIZE (this); 606 /* The rest of array sanitizations are done at run-time. */ 607 return_trace (c->check_struct (this) && machine.sanitize (c) && 608 hb_barrier () && 609 ligAction && component && ligature); 610 } 611 612 public: 613 StateTable<Types, EntryData> 614 machine; 615 protected: 616 NNOffsetTo<UnsizedArrayOf<HBUINT32>, HBUINT> 617 ligAction; /* Offset to the ligature action table. */ 618 NNOffsetTo<UnsizedArrayOf<HBUINT16>, HBUINT> 619 component; /* Offset to the component table. */ 620 NNOffsetTo<UnsizedArrayOf<HBGlyphID16>, HBUINT> 621 ligature; /* Offset to the actual ligature lists. */ 622 public: 623 DEFINE_SIZE_STATIC ((StateTable<Types, EntryData>::static_size + 3 * HBUINT::static_size)); 624 }; 625 626 template <typename Types> 627 struct NoncontextualSubtable 628 { 629 bool apply (hb_aat_apply_context_t *c) const 630 { 631 TRACE_APPLY (this); 632 633 bool ret = false; 634 unsigned int num_glyphs = c->face->get_num_glyphs (); 635 636 hb_glyph_info_t *info = c->buffer->info; 637 unsigned int count = c->buffer->len; 638 // If there's only one range, we already checked the flag. 639 auto *last_range = c->range_flags && (c->range_flags->length > 1) ? &(*c->range_flags)[0] : nullptr; 640 for (unsigned int i = 0; i < count; i++) 641 { 642 /* This block copied from StateTableDriver::drive. Keep in sync. */ 643 if (unlikely (last_range)) 644 { 645 auto *range = last_range; 646 { 647 unsigned cluster = info[i].cluster; 648 while (cluster < range->cluster_first) 649 range--; 650 while (cluster > range->cluster_last) 651 range++; 652 653 last_range = range; 654 } 655 if (!(range->flags & c->subtable_flags)) 656 continue; 657 } 658 659 const HBGlyphID16 *replacement = substitute.get_value (info[i].codepoint, num_glyphs); 660 if (replacement) 661 { 662 c->replace_glyph_inplace (i, *replacement); 663 ret = true; 664 } 665 } 666 667 return_trace (ret); 668 } 669 670 template <typename set_t> 671 void collect_initial_glyphs (set_t &glyphs, unsigned num_glyphs) const 672 { 673 substitute.collect_glyphs (glyphs, num_glyphs); 674 } 675 676 bool sanitize (hb_sanitize_context_t *c) const 677 { 678 TRACE_SANITIZE (this); 679 return_trace (substitute.sanitize (c)); 680 } 681 682 protected: 683 Lookup<HBGlyphID16> substitute; 684 public: 685 DEFINE_SIZE_MIN (2); 686 }; 687 688 template <typename Types> 689 struct InsertionSubtable 690 { 691 typedef typename Types::HBUINT HBUINT; 692 693 struct EntryData 694 { 695 HBUINT16 currentInsertIndex; /* Zero-based index into the insertion glyph table. 696 * The number of glyphs to be inserted is contained 697 * in the currentInsertCount field in the flags. 698 * A value of 0xFFFF indicates no insertion is to 699 * be done. */ 700 HBUINT16 markedInsertIndex; /* Zero-based index into the insertion glyph table. 701 * The number of glyphs to be inserted is contained 702 * in the markedInsertCount field in the flags. 703 * A value of 0xFFFF indicates no insertion is to 704 * be done. */ 705 public: 706 DEFINE_SIZE_STATIC (4); 707 }; 708 709 enum Flags 710 { 711 SetMark = 0x8000, /* If set, mark the current glyph. */ 712 DontAdvance = 0x4000, /* If set, don't advance to the next glyph before 713 * going to the new state. This does not mean 714 * that the glyph pointed to is the same one as 715 * before. If you've made insertions immediately 716 * downstream of the current glyph, the next glyph 717 * processed would in fact be the first one 718 * inserted. */ 719 CurrentIsKashidaLike= 0x2000, /* If set, and the currentInsertList is nonzero, 720 * then the specified glyph list will be inserted 721 * as a kashida-like insertion, either before or 722 * after the current glyph (depending on the state 723 * of the currentInsertBefore flag). If clear, and 724 * the currentInsertList is nonzero, then the 725 * specified glyph list will be inserted as a 726 * split-vowel-like insertion, either before or 727 * after the current glyph (depending on the state 728 * of the currentInsertBefore flag). */ 729 MarkedIsKashidaLike= 0x1000, /* If set, and the markedInsertList is nonzero, 730 * then the specified glyph list will be inserted 731 * as a kashida-like insertion, either before or 732 * after the marked glyph (depending on the state 733 * of the markedInsertBefore flag). If clear, and 734 * the markedInsertList is nonzero, then the 735 * specified glyph list will be inserted as a 736 * split-vowel-like insertion, either before or 737 * after the marked glyph (depending on the state 738 * of the markedInsertBefore flag). */ 739 CurrentInsertBefore= 0x0800, /* If set, specifies that insertions are to be made 740 * to the left of the current glyph. If clear, 741 * they're made to the right of the current glyph. */ 742 MarkedInsertBefore= 0x0400, /* If set, specifies that insertions are to be 743 * made to the left of the marked glyph. If clear, 744 * they're made to the right of the marked glyph. */ 745 CurrentInsertCount= 0x3E0, /* This 5-bit field is treated as a count of the 746 * number of glyphs to insert at the current 747 * position. Since zero means no insertions, the 748 * largest number of insertions at any given 749 * current location is 31 glyphs. */ 750 MarkedInsertCount= 0x001F, /* This 5-bit field is treated as a count of the 751 * number of glyphs to insert at the marked 752 * position. Since zero means no insertions, the 753 * largest number of insertions at any given 754 * marked location is 31 glyphs. */ 755 }; 756 757 bool is_action_initiable (const Entry<EntryData> &entry) const 758 { 759 return (entry.flags & SetMark); 760 } 761 bool is_actionable (const Entry<EntryData> &entry) const 762 { 763 return (entry.flags & (CurrentInsertCount | MarkedInsertCount)) && 764 (entry.data.currentInsertIndex != 0xFFFF ||entry.data.markedInsertIndex != 0xFFFF); 765 } 766 767 struct driver_context_t 768 { 769 static constexpr bool in_place = false; 770 771 driver_context_t (const InsertionSubtable *table_, 772 hb_aat_apply_context_t *c_) : 773 ret (false), 774 c (c_), 775 table (table_), 776 mark (0), 777 insertionAction (table+table->insertionAction) {} 778 779 void transition (hb_buffer_t *buffer, 780 StateTableDriver<Types, EntryData, Flags> *driver, 781 const Entry<EntryData> &entry) 782 { 783 unsigned int flags = entry.flags; 784 785 unsigned mark_loc = buffer->out_len; 786 787 if (entry.data.markedInsertIndex != 0xFFFF) 788 { 789 unsigned int count = (flags & MarkedInsertCount); 790 if (unlikely ((buffer->max_ops -= count) <= 0)) return; 791 unsigned int start = entry.data.markedInsertIndex; 792 const HBGlyphID16 *glyphs = &insertionAction[start]; 793 if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0; 794 hb_barrier (); 795 796 bool before = flags & MarkedInsertBefore; 797 798 unsigned int end = buffer->out_len; 799 if (unlikely (!buffer->move_to (mark))) return; 800 801 if (buffer->idx < buffer->len && !before) 802 if (unlikely (!buffer->copy_glyph ())) return; 803 /* TODO We ignore KashidaLike setting. */ 804 if (unlikely (!c->output_glyphs (count, glyphs))) return; 805 ret = true; 806 if (buffer->idx < buffer->len && !before) 807 buffer->skip_glyph (); 808 809 if (unlikely (!buffer->move_to (end + count))) return; 810 811 buffer->unsafe_to_break_from_outbuffer (mark, hb_min (buffer->idx + 1, buffer->len)); 812 } 813 814 if (flags & SetMark) 815 mark = mark_loc; 816 817 if (entry.data.currentInsertIndex != 0xFFFF) 818 { 819 unsigned int count = (flags & CurrentInsertCount) >> 5; 820 if (unlikely ((buffer->max_ops -= count) <= 0)) return; 821 unsigned int start = entry.data.currentInsertIndex; 822 const HBGlyphID16 *glyphs = &insertionAction[start]; 823 if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0; 824 hb_barrier (); 825 826 bool before = flags & CurrentInsertBefore; 827 828 unsigned int end = buffer->out_len; 829 830 if (buffer->idx < buffer->len && !before) 831 if (unlikely (!buffer->copy_glyph ())) return; 832 /* TODO We ignore KashidaLike setting. */ 833 if (unlikely (!c->output_glyphs (count, glyphs))) return; 834 ret = true; 835 if (buffer->idx < buffer->len && !before) 836 buffer->skip_glyph (); 837 838 /* Humm. Not sure where to move to. There's this wording under 839 * DontAdvance flag: 840 * 841 * "If set, don't update the glyph index before going to the new state. 842 * This does not mean that the glyph pointed to is the same one as 843 * before. If you've made insertions immediately downstream of the 844 * current glyph, the next glyph processed would in fact be the first 845 * one inserted." 846 * 847 * This suggests that if DontAdvance is NOT set, we should move to 848 * end+count. If it *was*, then move to end, such that newly inserted 849 * glyphs are now visible. 850 * 851 * https://github.com/harfbuzz/harfbuzz/issues/1224#issuecomment-427691417 852 */ 853 if (unlikely (!buffer->move_to ((flags & DontAdvance) ? end : end + count))) return; 854 } 855 } 856 857 public: 858 bool ret; 859 hb_aat_apply_context_t *c; 860 const InsertionSubtable *table; 861 private: 862 unsigned int mark; 863 const UnsizedArrayOf<HBGlyphID16> &insertionAction; 864 }; 865 866 bool apply (hb_aat_apply_context_t *c) const 867 { 868 TRACE_APPLY (this); 869 870 driver_context_t dc (this, c); 871 872 StateTableDriver<Types, EntryData, Flags> driver (machine, c->face); 873 874 driver.drive (&dc, c); 875 876 return_trace (dc.ret); 877 } 878 879 bool sanitize (hb_sanitize_context_t *c) const 880 { 881 TRACE_SANITIZE (this); 882 /* The rest of array sanitizations are done at run-time. */ 883 return_trace (c->check_struct (this) && machine.sanitize (c) && 884 hb_barrier () && 885 insertionAction); 886 } 887 888 public: 889 StateTable<Types, EntryData> 890 machine; 891 protected: 892 NNOffsetTo<UnsizedArrayOf<HBGlyphID16>, HBUINT> 893 insertionAction; /* Byte offset from stateHeader to the start of 894 * the insertion glyph table. */ 895 public: 896 DEFINE_SIZE_STATIC ((StateTable<Types, EntryData>::static_size + HBUINT::static_size)); 897 }; 898 899 900 struct Feature 901 { 902 bool sanitize (hb_sanitize_context_t *c) const 903 { 904 TRACE_SANITIZE (this); 905 return_trace (c->check_struct (this)); 906 } 907 908 public: 909 HBUINT16 featureType; /* The type of feature. */ 910 HBUINT16 featureSetting; /* The feature's setting (aka selector). */ 911 HBUINT32 enableFlags; /* Flags for the settings that this feature 912 * and setting enables. */ 913 HBUINT32 disableFlags; /* Complement of flags for the settings that this 914 * feature and setting disable. */ 915 916 public: 917 DEFINE_SIZE_STATIC (12); 918 }; 919 920 921 struct hb_accelerate_subtables_context_t : 922 hb_dispatch_context_t<hb_accelerate_subtables_context_t> 923 { 924 struct hb_applicable_t 925 { 926 friend struct hb_accelerate_subtables_context_t; 927 friend struct hb_aat_layout_lookup_accelerator_t; 928 929 public: 930 hb_bit_set_t glyph_set; 931 mutable hb_aat_class_cache_t class_cache; 932 933 template <typename T> 934 auto init_ (const T &obj_, unsigned num_glyphs, hb_priority<1>) HB_AUTO_RETURN 935 ( 936 obj_.machine.collect_initial_glyphs (glyph_set, num_glyphs, obj_) 937 ) 938 939 template <typename T> 940 void init_ (const T &obj_, unsigned num_glyphs, hb_priority<0>) 941 { 942 obj_.collect_initial_glyphs (glyph_set, num_glyphs); 943 } 944 945 template <typename T> 946 void init (const T &obj_, unsigned num_glyphs) 947 { 948 glyph_set.init (); 949 init_ (obj_, num_glyphs, hb_prioritize); 950 class_cache.clear (); 951 } 952 953 void 954 fini () 955 { 956 glyph_set.fini (); 957 } 958 }; 959 960 /* Dispatch interface. */ 961 template <typename T> 962 return_t dispatch (const T &obj) 963 { 964 hb_applicable_t *entry = &array[i++]; 965 966 entry->init (obj, num_glyphs); 967 968 return hb_empty_t (); 969 } 970 static return_t default_return_value () { return hb_empty_t (); } 971 972 bool stop_sublookup_iteration (return_t r) const { return false; } 973 974 hb_accelerate_subtables_context_t (hb_applicable_t *array_, unsigned num_glyphs_) : 975 hb_dispatch_context_t<hb_accelerate_subtables_context_t> (), 976 array (array_), num_glyphs (num_glyphs_) {} 977 978 hb_applicable_t *array; 979 unsigned num_glyphs; 980 unsigned i = 0; 981 }; 982 983 struct hb_aat_layout_chain_accelerator_t 984 { 985 template <typename TChain> 986 static hb_aat_layout_chain_accelerator_t *create (const TChain &chain, unsigned num_glyphs) 987 { 988 unsigned count = chain.get_subtable_count (); 989 990 unsigned size = sizeof (hb_aat_layout_chain_accelerator_t) - 991 HB_VAR_ARRAY * sizeof (hb_accelerate_subtables_context_t::hb_applicable_t) + 992 count * sizeof (hb_accelerate_subtables_context_t::hb_applicable_t); 993 994 /* The following is a calloc because when we are collecting subtables, 995 * some of them might be invalid and hence not collect; as a result, 996 * we might not fill in all the count entries of the subtables array. 997 * Zeroing it allows the set digest to gatekeep it without having to 998 * initialize it further. */ 999 auto *thiz = (hb_aat_layout_chain_accelerator_t *) hb_calloc (1, size); 1000 if (unlikely (!thiz)) 1001 return nullptr; 1002 1003 thiz->count = count; 1004 1005 hb_accelerate_subtables_context_t c_accelerate_subtables (thiz->subtables, num_glyphs); 1006 chain.dispatch (&c_accelerate_subtables); 1007 1008 return thiz; 1009 } 1010 1011 void destroy () 1012 { 1013 for (unsigned i = 0; i < count; i++) 1014 subtables[i].fini (); 1015 } 1016 1017 unsigned count; 1018 hb_accelerate_subtables_context_t::hb_applicable_t subtables[HB_VAR_ARRAY]; 1019 }; 1020 1021 template <typename Types> 1022 struct ChainSubtable 1023 { 1024 typedef typename Types::HBUINT HBUINT; 1025 1026 template <typename T> 1027 friend struct Chain; 1028 1029 unsigned int get_size () const { return length; } 1030 unsigned int get_type () const { return coverage & 0xFF; } 1031 unsigned int get_coverage () const { return coverage >> (sizeof (HBUINT) * 8 - 8); } 1032 1033 enum Coverage 1034 { 1035 Vertical = 0x80, /* If set, this subtable will only be applied 1036 * to vertical text. If clear, this subtable 1037 * will only be applied to horizontal text. */ 1038 Backwards = 0x40, /* If set, this subtable will process glyphs 1039 * in descending order. If clear, it will 1040 * process the glyphs in ascending order. */ 1041 AllDirections = 0x20, /* If set, this subtable will be applied to 1042 * both horizontal and vertical text (i.e. 1043 * the state of bit 0x80000000 is ignored). */ 1044 Logical = 0x10, /* If set, this subtable will process glyphs 1045 * in logical order (or reverse logical order, 1046 * depending on the value of bit 0x80000000). */ 1047 }; 1048 enum Type 1049 { 1050 Rearrangement = 0, 1051 Contextual = 1, 1052 Ligature = 2, 1053 Noncontextual = 4, 1054 Insertion = 5 1055 }; 1056 1057 template <typename context_t, typename ...Ts> 1058 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const 1059 { 1060 unsigned int subtable_type = get_type (); 1061 TRACE_DISPATCH (this, subtable_type); 1062 switch (subtable_type) { 1063 case Rearrangement: return_trace (c->dispatch (u.rearrangement, std::forward<Ts> (ds)...)); 1064 case Contextual: return_trace (c->dispatch (u.contextual, std::forward<Ts> (ds)...)); 1065 case Ligature: return_trace (c->dispatch (u.ligature, std::forward<Ts> (ds)...)); 1066 case Noncontextual: return_trace (c->dispatch (u.noncontextual, std::forward<Ts> (ds)...)); 1067 case Insertion: return_trace (c->dispatch (u.insertion, std::forward<Ts> (ds)...)); 1068 default: return_trace (c->default_return_value ()); 1069 } 1070 } 1071 1072 bool apply (hb_aat_apply_context_t *c) const 1073 { 1074 TRACE_APPLY (this); 1075 // Disabled for https://github.com/harfbuzz/harfbuzz/issues/4873 1076 //hb_sanitize_with_object_t with (&c->sanitizer, this); 1077 return_trace (dispatch (c)); 1078 } 1079 1080 bool sanitize (hb_sanitize_context_t *c) const 1081 { 1082 TRACE_SANITIZE (this); 1083 if (!(length.sanitize (c) && 1084 hb_barrier () && 1085 length >= min_size && 1086 c->check_range (this, length))) 1087 return_trace (false); 1088 1089 // Disabled for https://github.com/harfbuzz/harfbuzz/issues/4873 1090 //hb_sanitize_with_object_t with (c, this); 1091 return_trace (dispatch (c)); 1092 } 1093 1094 protected: 1095 HBUINT length; /* Total subtable length, including this header. */ 1096 HBUINT coverage; /* Coverage flags and subtable type. */ 1097 HBUINT32 subFeatureFlags;/* The 32-bit mask identifying which subtable this is. */ 1098 union { 1099 RearrangementSubtable<Types> rearrangement; 1100 ContextualSubtable<Types> contextual; 1101 LigatureSubtable<Types> ligature; 1102 NoncontextualSubtable<Types> noncontextual; 1103 InsertionSubtable<Types> insertion; 1104 } u; 1105 public: 1106 DEFINE_SIZE_MIN (2 * sizeof (HBUINT) + 4); 1107 }; 1108 1109 template <typename Types> 1110 struct Chain 1111 { 1112 typedef typename Types::HBUINT HBUINT; 1113 1114 unsigned get_subtable_count () const { return subtableCount; } 1115 1116 hb_mask_t compile_flags (const hb_aat_map_builder_t *map) const 1117 { 1118 hb_mask_t flags = defaultFlags; 1119 { 1120 unsigned int count = featureCount; 1121 for (unsigned i = 0; i < count; i++) 1122 { 1123 const Feature &feature = featureZ[i]; 1124 hb_aat_layout_feature_type_t type = (hb_aat_layout_feature_type_t) (unsigned int) feature.featureType; 1125 hb_aat_layout_feature_selector_t setting = (hb_aat_layout_feature_selector_t) (unsigned int) feature.featureSetting; 1126 retry: 1127 // Check whether this type/setting pair was requested in the map, and if so, apply its flags. 1128 // (The search here only looks at the type and setting fields of feature_info_t.) 1129 hb_aat_map_builder_t::feature_info_t info = { type, setting, false, 0 }; 1130 if (map->current_features.bsearch (info)) 1131 { 1132 flags &= feature.disableFlags; 1133 flags |= feature.enableFlags; 1134 } 1135 else if (type == HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE && setting == HB_AAT_LAYOUT_FEATURE_SELECTOR_SMALL_CAPS) 1136 { 1137 /* Deprecated. https://github.com/harfbuzz/harfbuzz/issues/1342 */ 1138 type = HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE; 1139 setting = HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS; 1140 goto retry; 1141 } 1142 #ifndef HB_NO_AAT 1143 else if (type == HB_AAT_LAYOUT_FEATURE_TYPE_LANGUAGE_TAG_TYPE && setting && 1144 /* TODO: Rudimentary language matching. */ 1145 hb_language_matches (map->face->table.ltag->get_language (setting - 1), map->props.language)) 1146 { 1147 flags &= feature.disableFlags; 1148 flags |= feature.enableFlags; 1149 } 1150 #endif 1151 } 1152 } 1153 return flags; 1154 } 1155 1156 void apply (hb_aat_apply_context_t *c, 1157 const hb_aat_layout_chain_accelerator_t *accel) const 1158 { 1159 const ChainSubtable<Types> *subtable = &StructAfter<ChainSubtable<Types>> (featureZ.as_array (featureCount)); 1160 unsigned int count = subtableCount; 1161 for (unsigned int i = 0; i < count; i++) 1162 { 1163 bool reverse; 1164 1165 auto coverage = subtable->get_coverage (); 1166 1167 hb_mask_t subtable_flags = subtable->subFeatureFlags; 1168 if (hb_none (hb_iter (c->range_flags) | 1169 hb_map ([subtable_flags] (const hb_aat_map_t::range_flags_t _) -> bool { return subtable_flags & (_.flags); }))) 1170 goto skip; 1171 1172 if (!(coverage & ChainSubtable<Types>::AllDirections) && 1173 HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) != 1174 bool (coverage & ChainSubtable<Types>::Vertical)) 1175 goto skip; 1176 1177 c->subtable_flags = subtable_flags; 1178 c->first_set = accel ? &accel->subtables[i].glyph_set : &Null(hb_bit_set_t); 1179 c->machine_class_cache = accel ? &accel->subtables[i].class_cache : nullptr; 1180 1181 if (!c->buffer_intersects_machine ()) 1182 { 1183 (void) c->buffer->message (c->font, "skipped chainsubtable %u because no glyph matches", c->lookup_index); 1184 goto skip; 1185 } 1186 1187 /* Buffer contents is always in logical direction. Determine if 1188 * we need to reverse before applying this subtable. We reverse 1189 * back after if we did reverse indeed. 1190 * 1191 * Quoting the spac: 1192 * """ 1193 * Bits 28 and 30 of the coverage field control the order in which 1194 * glyphs are processed when the subtable is run by the layout engine. 1195 * Bit 28 is used to indicate if the glyph processing direction is 1196 * the same as logical order or layout order. Bit 30 is used to 1197 * indicate whether glyphs are processed forwards or backwards within 1198 * that order. 1199 1200 Bit 30 Bit 28 Interpretation for Horizontal Text 1201 0 0 The subtable is processed in layout order 1202 (the same order as the glyphs, which is 1203 always left-to-right). 1204 1 0 The subtable is processed in reverse layout order 1205 (the order opposite that of the glyphs, which is 1206 always right-to-left). 1207 0 1 The subtable is processed in logical order 1208 (the same order as the characters, which may be 1209 left-to-right or right-to-left). 1210 1 1 The subtable is processed in reverse logical order 1211 (the order opposite that of the characters, which 1212 may be right-to-left or left-to-right). 1213 */ 1214 reverse = coverage & ChainSubtable<Types>::Logical ? 1215 bool (coverage & ChainSubtable<Types>::Backwards) : 1216 bool (coverage & ChainSubtable<Types>::Backwards) != 1217 HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction); 1218 1219 if (!c->buffer->message (c->font, "start chainsubtable %u", c->lookup_index)) 1220 goto skip; 1221 1222 if (reverse != c->buffer_is_reversed) 1223 c->reverse_buffer (); 1224 1225 subtable->apply (c); 1226 1227 (void) c->buffer->message (c->font, "end chainsubtable %u", c->lookup_index); 1228 1229 if (unlikely (!c->buffer->successful)) break; 1230 1231 skip: 1232 subtable = &StructAfter<ChainSubtable<Types>> (*subtable); 1233 c->set_lookup_index (c->lookup_index + 1); 1234 } 1235 if (c->buffer_is_reversed) 1236 c->reverse_buffer (); 1237 } 1238 1239 unsigned int get_size () const { return length; } 1240 1241 template <typename context_t, typename ...Ts> 1242 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const 1243 { 1244 const ChainSubtable<Types> *subtable = &StructAfter<ChainSubtable<Types>> (featureZ.as_array (featureCount)); 1245 unsigned int count = subtableCount; 1246 for (unsigned int i = 0; i < count; i++) 1247 { 1248 typename context_t::return_t ret = subtable->dispatch (c, std::forward<Ts> (ds)...); 1249 if (c->stop_sublookup_iteration (ret)) 1250 return ret; 1251 subtable = &StructAfter<ChainSubtable<Types>> (*subtable); 1252 } 1253 return c->default_return_value (); 1254 } 1255 1256 bool sanitize (hb_sanitize_context_t *c, unsigned int version) const 1257 { 1258 TRACE_SANITIZE (this); 1259 if (!(length.sanitize (c) && 1260 hb_barrier () && 1261 length >= min_size && 1262 c->check_range (this, length))) 1263 return_trace (false); 1264 1265 if (!c->check_array (featureZ.arrayZ, featureCount)) 1266 return_trace (false); 1267 1268 const ChainSubtable<Types> *subtable = &StructAfter<ChainSubtable<Types>> (featureZ.as_array (featureCount)); 1269 unsigned int count = subtableCount; 1270 for (unsigned int i = 0; i < count; i++) 1271 { 1272 if (!subtable->sanitize (c)) 1273 return_trace (false); 1274 hb_barrier (); 1275 subtable = &StructAfter<ChainSubtable<Types>> (*subtable); 1276 } 1277 1278 if (version >= 3) 1279 { 1280 const SubtableGlyphCoverage *coverage = (const SubtableGlyphCoverage *) subtable; 1281 if (!coverage->sanitize (c, count)) 1282 return_trace (false); 1283 } 1284 1285 return_trace (true); 1286 } 1287 1288 protected: 1289 HBUINT32 defaultFlags; /* The default specification for subtables. */ 1290 HBUINT32 length; /* Total byte count, including this header. */ 1291 HBUINT featureCount; /* Number of feature subtable entries. */ 1292 HBUINT subtableCount; /* The number of subtables in the chain. */ 1293 1294 UnsizedArrayOf<Feature> featureZ; /* Features. */ 1295 /*ChainSubtable firstSubtable;*//* Subtables. */ 1296 /*SubtableGlyphCoverage coverages*//* Only if version >= 3. */ 1297 1298 public: 1299 DEFINE_SIZE_MIN (8 + 2 * sizeof (HBUINT)); 1300 }; 1301 1302 1303 /* 1304 * The 'mort'/'morx' Table 1305 */ 1306 1307 template <typename T, typename Types, hb_tag_t TAG> 1308 struct mortmorx 1309 { 1310 static constexpr hb_tag_t tableTag = TAG; 1311 1312 bool has_data () const { return version != 0; } 1313 1314 struct accelerator_t 1315 { 1316 accelerator_t (hb_face_t *face) 1317 { 1318 hb_sanitize_context_t sc; 1319 this->table = sc.reference_table<T> (face); 1320 1321 if (unlikely (this->table->is_blocklisted (this->table.get_blob (), face))) 1322 { 1323 hb_blob_destroy (this->table.get_blob ()); 1324 this->table = hb_blob_get_empty (); 1325 } 1326 1327 this->chain_count = table->get_chain_count (); 1328 1329 this->accels = (hb_atomic_t<hb_aat_layout_chain_accelerator_t *> *) hb_calloc (this->chain_count, sizeof (*accels)); 1330 if (unlikely (!this->accels)) 1331 { 1332 this->chain_count = 0; 1333 this->table.destroy (); 1334 this->table = hb_blob_get_empty (); 1335 } 1336 } 1337 ~accelerator_t () 1338 { 1339 for (unsigned int i = 0; i < this->chain_count; i++) 1340 { 1341 if (this->accels[i]) 1342 this->accels[i]->destroy (); 1343 hb_free (this->accels[i]); 1344 } 1345 hb_free (this->accels); 1346 this->table.destroy (); 1347 } 1348 1349 hb_blob_t *get_blob () const { return table.get_blob (); } 1350 1351 template <typename Chain> 1352 hb_aat_layout_chain_accelerator_t *get_accel (unsigned chain_index, const Chain &chain, unsigned num_glyphs) const 1353 { 1354 if (unlikely (chain_index >= chain_count)) return nullptr; 1355 1356 retry: 1357 auto *accel = accels[chain_index].get_acquire (); 1358 if (unlikely (!accel)) 1359 { 1360 accel = hb_aat_layout_chain_accelerator_t::create (chain, num_glyphs); 1361 if (unlikely (!accel)) 1362 return nullptr; 1363 1364 if (unlikely (!accels[chain_index].cmpexch (nullptr, accel))) 1365 { 1366 hb_free (accel); 1367 goto retry; 1368 } 1369 } 1370 1371 return accel; 1372 } 1373 1374 hb_blob_ptr_t<T> table; 1375 unsigned int chain_count; 1376 hb_atomic_t<hb_aat_layout_chain_accelerator_t *> *accels; 1377 hb_aat_scratch_t scratch; 1378 }; 1379 1380 1381 void compile_flags (const hb_aat_map_builder_t *mapper, 1382 hb_aat_map_t *map) const 1383 { 1384 const Chain<Types> *chain = &firstChain; 1385 unsigned int count = chainCount; 1386 if (unlikely (!map->chain_flags.resize (count))) 1387 return; 1388 for (unsigned int i = 0; i < count; i++) 1389 { 1390 map->chain_flags[i].push (hb_aat_map_t::range_flags_t {chain->compile_flags (mapper), 1391 mapper->range_first, 1392 mapper->range_last}); 1393 chain = &StructAfter<Chain<Types>> (*chain); 1394 } 1395 } 1396 1397 unsigned get_chain_count () const 1398 { 1399 return chainCount; 1400 } 1401 void apply (hb_aat_apply_context_t *c, 1402 const hb_aat_map_t &map, 1403 const accelerator_t &accel) const 1404 { 1405 if (unlikely (!c->buffer->successful)) return; 1406 1407 c->buffer->unsafe_to_concat (); 1408 1409 c->setup_buffer_glyph_set (); 1410 1411 c->set_lookup_index (0); 1412 const Chain<Types> *chain = &firstChain; 1413 unsigned int count = chainCount; 1414 for (unsigned int i = 0; i < count; i++) 1415 { 1416 auto *chain_accel = accel.get_accel (i, *chain, c->face->get_num_glyphs ()); 1417 c->range_flags = &map.chain_flags[i]; 1418 chain->apply (c, chain_accel); 1419 if (unlikely (!c->buffer->successful)) return; 1420 chain = &StructAfter<Chain<Types>> (*chain); 1421 } 1422 } 1423 1424 bool sanitize (hb_sanitize_context_t *c) const 1425 { 1426 TRACE_SANITIZE (this); 1427 if (!(version.sanitize (c) && 1428 hb_barrier () && 1429 version && 1430 chainCount.sanitize (c))) 1431 return_trace (false); 1432 1433 const Chain<Types> *chain = &firstChain; 1434 unsigned int count = chainCount; 1435 for (unsigned int i = 0; i < count; i++) 1436 { 1437 if (!chain->sanitize (c, version)) 1438 return_trace (false); 1439 hb_barrier (); 1440 chain = &StructAfter<Chain<Types>> (*chain); 1441 } 1442 1443 return_trace (true); 1444 } 1445 1446 protected: 1447 HBUINT16 version; /* Version number of the glyph metamorphosis table. 1448 * 1, 2, or 3. */ 1449 HBUINT16 unused; /* Set to 0. */ 1450 HBUINT32 chainCount; /* Number of metamorphosis chains contained in this 1451 * table. */ 1452 Chain<Types> firstChain; /* Chains. */ 1453 1454 public: 1455 DEFINE_SIZE_MIN (8); 1456 }; 1457 1458 struct morx : mortmorx<morx, ExtendedTypes, HB_AAT_TAG_morx> 1459 { 1460 HB_INTERNAL bool is_blocklisted (hb_blob_t *blob, 1461 hb_face_t *face) const; 1462 }; 1463 1464 struct mort : mortmorx<mort, ObsoleteTypes, HB_AAT_TAG_mort> 1465 { 1466 HB_INTERNAL bool is_blocklisted (hb_blob_t *blob, 1467 hb_face_t *face) const; 1468 }; 1469 1470 struct morx_accelerator_t : morx::accelerator_t { 1471 morx_accelerator_t (hb_face_t *face) : morx::accelerator_t (face) {} 1472 }; 1473 struct mort_accelerator_t : mort::accelerator_t { 1474 mort_accelerator_t (hb_face_t *face) : mort::accelerator_t (face) {} 1475 }; 1476 1477 1478 } /* namespace AAT */ 1479 1480 1481 #endif /* HB_AAT_LAYOUT_MORX_TABLE_HH */