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-common.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 struct driver_context_t 56 { 57 enum { in_place = true }; 58 enum Flags 59 { 60 MarkFirst = 0x8000, /* If set, make the current glyph the first 61 * glyph to be rearranged. */ 62 DontAdvance = 0x4000, /* If set, don't advance to the next glyph 63 * before going to the new state. This means 64 * that the glyph index doesn't change, even 65 * if the glyph at that index has changed. */ 66 MarkLast = 0x2000, /* If set, make the current glyph the last 67 * glyph to be rearranged. */ 68 Reserved = 0x1FF0, /* These bits are reserved and should be set to 0. */ 69 Verb = 0x000F, /* The type of rearrangement specified. */ 70 }; 71 driver_context_tAAT::RearrangementSubtable::driver_context_t72 driver_context_t (const RearrangementSubtable *table HB_UNUSED) : 73 ret (false), 74 start (0), end (0) {} 75 is_actionableAAT::RearrangementSubtable::driver_context_t76 bool is_actionable (StateTableDriver<Types, EntryData> *driver HB_UNUSED, 77 const Entry<EntryData> *entry) 78 { 79 return (entry->flags & Verb) && start < end; 80 } transitionAAT::RearrangementSubtable::driver_context_t81 bool transition (StateTableDriver<Types, EntryData> *driver, 82 const Entry<EntryData> *entry) 83 { 84 hb_buffer_t *buffer = driver->buffer; 85 unsigned int flags = entry->flags; 86 87 if (flags & MarkFirst) 88 start = buffer->idx; 89 90 if (flags & MarkLast) 91 end = MIN (buffer->idx + 1, buffer->len); 92 93 if ((flags & Verb) && start < end) 94 { 95 /* The following map has two nibbles, for start-side 96 * and end-side. Values of 0,1,2 mean move that many 97 * to the other side. Value of 3 means move 2 and 98 * flip them. */ 99 const unsigned char map[16] = 100 { 101 0x00, /* 0 no change */ 102 0x10, /* 1 Ax => xA */ 103 0x01, /* 2 xD => Dx */ 104 0x11, /* 3 AxD => DxA */ 105 0x20, /* 4 ABx => xAB */ 106 0x30, /* 5 ABx => xBA */ 107 0x02, /* 6 xCD => CDx */ 108 0x03, /* 7 xCD => DCx */ 109 0x12, /* 8 AxCD => CDxA */ 110 0x13, /* 9 AxCD => DCxA */ 111 0x21, /* 10 ABxD => DxAB */ 112 0x31, /* 11 ABxD => DxBA */ 113 0x22, /* 12 ABxCD => CDxAB */ 114 0x32, /* 13 ABxCD => CDxBA */ 115 0x23, /* 14 ABxCD => DCxAB */ 116 0x33, /* 15 ABxCD => DCxBA */ 117 }; 118 119 unsigned int m = map[flags & Verb]; 120 unsigned int l = MIN<unsigned int> (2, m >> 4); 121 unsigned int r = MIN<unsigned int> (2, m & 0x0F); 122 bool reverse_l = 3 == (m >> 4); 123 bool reverse_r = 3 == (m & 0x0F); 124 125 if (end - start >= l + r) 126 { 127 buffer->merge_clusters (start, MIN (buffer->idx + 1, buffer->len)); 128 buffer->merge_clusters (start, end); 129 130 hb_glyph_info_t *info = buffer->info; 131 hb_glyph_info_t buf[4]; 132 133 memcpy (buf, info + start, l * sizeof (buf[0])); 134 memcpy (buf + 2, info + end - r, r * sizeof (buf[0])); 135 136 if (l != r) 137 memmove (info + start + r, info + start + l, (end - start - l - r) * sizeof (buf[0])); 138 139 memcpy (info + start, buf + 2, r * sizeof (buf[0])); 140 memcpy (info + end - l, buf, l * sizeof (buf[0])); 141 if (reverse_l) 142 { 143 buf[0] = info[end - 1]; 144 info[end - 1] = info[end - 2]; 145 info[end - 2] = buf[0]; 146 } 147 if (reverse_r) 148 { 149 buf[0] = info[start]; 150 info[start] = info[start + 1]; 151 info[start + 1] = buf[0]; 152 } 153 } 154 } 155 156 return true; 157 } 158 159 public: 160 bool ret; 161 private: 162 unsigned int start; 163 unsigned int end; 164 }; 165 applyAAT::RearrangementSubtable166 bool apply (hb_aat_apply_context_t *c) const 167 { 168 TRACE_APPLY (this); 169 170 driver_context_t dc (this); 171 172 StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->face); 173 driver.drive (&dc); 174 175 return_trace (dc.ret); 176 } 177 sanitizeAAT::RearrangementSubtable178 bool sanitize (hb_sanitize_context_t *c) const 179 { 180 TRACE_SANITIZE (this); 181 return_trace (machine.sanitize (c)); 182 } 183 184 protected: 185 StateTable<Types, EntryData> machine; 186 public: 187 DEFINE_SIZE_STATIC (16); 188 }; 189 190 template <typename Types> 191 struct ContextualSubtable 192 { 193 typedef typename Types::HBUINT HBUINT; 194 195 struct EntryData 196 { 197 HBUINT16 markIndex; /* Index of the substitution table for the 198 * marked glyph (use 0xFFFF for none). */ 199 HBUINT16 currentIndex; /* Index of the substitution table for the 200 * current glyph (use 0xFFFF for none). */ 201 public: 202 DEFINE_SIZE_STATIC (4); 203 }; 204 205 struct driver_context_t 206 { 207 enum { in_place = true }; 208 enum Flags 209 { 210 SetMark = 0x8000, /* If set, make the current glyph the marked glyph. */ 211 DontAdvance = 0x4000, /* If set, don't advance to the next glyph before 212 * going to the new state. */ 213 Reserved = 0x3FFF, /* These bits are reserved and should be set to 0. */ 214 }; 215 driver_context_tAAT::ContextualSubtable::driver_context_t216 driver_context_t (const ContextualSubtable *table_, 217 hb_aat_apply_context_t *c_) : 218 ret (false), 219 c (c_), 220 mark_set (false), 221 mark (0), 222 table (table_), 223 subs (table+table->substitutionTables) {} 224 is_actionableAAT::ContextualSubtable::driver_context_t225 bool is_actionable (StateTableDriver<Types, EntryData> *driver, 226 const Entry<EntryData> *entry) 227 { 228 hb_buffer_t *buffer = driver->buffer; 229 230 if (buffer->idx == buffer->len && !mark_set) 231 return false; 232 233 return entry->data.markIndex != 0xFFFF || entry->data.currentIndex != 0xFFFF; 234 } transitionAAT::ContextualSubtable::driver_context_t235 bool transition (StateTableDriver<Types, EntryData> *driver, 236 const Entry<EntryData> *entry) 237 { 238 hb_buffer_t *buffer = driver->buffer; 239 240 /* Looks like CoreText applies neither mark nor current substitution for 241 * end-of-text if mark was not explicitly set. */ 242 if (buffer->idx == buffer->len && !mark_set) 243 return true; 244 245 const GlyphID *replacement; 246 247 replacement = nullptr; 248 if (Types::extended) 249 { 250 if (entry->data.markIndex != 0xFFFF) 251 { 252 const Lookup<GlyphID> &lookup = subs[entry->data.markIndex]; 253 replacement = lookup.get_value (buffer->info[mark].codepoint, driver->num_glyphs); 254 } 255 } 256 else 257 { 258 unsigned int offset = entry->data.markIndex + buffer->info[mark].codepoint; 259 const UnsizedArrayOf<GlyphID> &subs_old = (const UnsizedArrayOf<GlyphID> &) subs; 260 replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)]; 261 if (!replacement->sanitize (&c->sanitizer) || !*replacement) 262 replacement = nullptr; 263 } 264 if (replacement) 265 { 266 buffer->unsafe_to_break (mark, MIN (buffer->idx + 1, buffer->len)); 267 buffer->info[mark].codepoint = *replacement; 268 ret = true; 269 } 270 271 replacement = nullptr; 272 unsigned int idx = MIN (buffer->idx, buffer->len - 1); 273 if (Types::extended) 274 { 275 if (entry->data.currentIndex != 0xFFFF) 276 { 277 const Lookup<GlyphID> &lookup = subs[entry->data.currentIndex]; 278 replacement = lookup.get_value (buffer->info[idx].codepoint, driver->num_glyphs); 279 } 280 } 281 else 282 { 283 unsigned int offset = entry->data.currentIndex + buffer->info[idx].codepoint; 284 const UnsizedArrayOf<GlyphID> &subs_old = (const UnsizedArrayOf<GlyphID> &) subs; 285 replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)]; 286 if (!replacement->sanitize (&c->sanitizer) || !*replacement) 287 replacement = nullptr; 288 } 289 if (replacement) 290 { 291 buffer->info[idx].codepoint = *replacement; 292 ret = true; 293 } 294 295 if (entry->flags & SetMark) 296 { 297 mark_set = true; 298 mark = buffer->idx; 299 } 300 301 return true; 302 } 303 304 public: 305 bool ret; 306 private: 307 hb_aat_apply_context_t *c; 308 bool mark_set; 309 unsigned int mark; 310 const ContextualSubtable *table; 311 const UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT, false> &subs; 312 }; 313 applyAAT::ContextualSubtable314 bool apply (hb_aat_apply_context_t *c) const 315 { 316 TRACE_APPLY (this); 317 318 driver_context_t dc (this, c); 319 320 StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->face); 321 driver.drive (&dc); 322 323 return_trace (dc.ret); 324 } 325 sanitizeAAT::ContextualSubtable326 bool sanitize (hb_sanitize_context_t *c) const 327 { 328 TRACE_SANITIZE (this); 329 330 unsigned int num_entries = 0; 331 if (unlikely (!machine.sanitize (c, &num_entries))) return_trace (false); 332 333 if (!Types::extended) return_trace (true); 334 335 unsigned int num_lookups = 0; 336 337 const Entry<EntryData> *entries = machine.get_entries (); 338 for (unsigned int i = 0; i < num_entries; i++) 339 { 340 const EntryData &data = entries[i].data; 341 342 if (data.markIndex != 0xFFFF) 343 num_lookups = MAX<unsigned int> (num_lookups, 1 + data.markIndex); 344 if (data.currentIndex != 0xFFFF) 345 num_lookups = MAX<unsigned int> (num_lookups, 1 + data.currentIndex); 346 } 347 348 return_trace (substitutionTables.sanitize (c, this, num_lookups)); 349 } 350 351 protected: 352 StateTable<Types, EntryData> 353 machine; 354 OffsetTo<UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT, false>, HBUINT, false> 355 substitutionTables; 356 public: 357 DEFINE_SIZE_STATIC (20); 358 }; 359 360 361 template <bool extended> 362 struct LigatureEntry; 363 364 template <> 365 struct LigatureEntry<true> 366 { 367 enum Flags 368 { 369 SetComponent = 0x8000, /* Push this glyph onto the component stack for 370 * eventual processing. */ 371 DontAdvance = 0x4000, /* Leave the glyph pointer at this glyph for the 372 next iteration. */ 373 PerformAction = 0x2000, /* Use the ligActionIndex to process a ligature 374 * group. */ 375 Reserved = 0x1FFF, /* These bits are reserved and should be set to 0. */ 376 }; 377 378 struct EntryData 379 { 380 HBUINT16 ligActionIndex; /* Index to the first ligActionTable entry 381 * for processing this group, if indicated 382 * by the flags. */ 383 public: 384 DEFINE_SIZE_STATIC (2); 385 }; 386 performActionAAT::LigatureEntry387 static bool performAction (const Entry<EntryData> *entry) 388 { return entry->flags & PerformAction; } 389 ligActionIndexAAT::LigatureEntry390 static unsigned int ligActionIndex (const Entry<EntryData> *entry) 391 { return entry->data.ligActionIndex; } 392 }; 393 template <> 394 struct LigatureEntry<false> 395 { 396 enum Flags 397 { 398 SetComponent = 0x8000, /* Push this glyph onto the component stack for 399 * eventual processing. */ 400 DontAdvance = 0x4000, /* Leave the glyph pointer at this glyph for the 401 next iteration. */ 402 Offset = 0x3FFF, /* Byte offset from beginning of subtable to the 403 * ligature action list. This value must be a 404 * multiple of 4. */ 405 }; 406 407 typedef void EntryData; 408 performActionAAT::LigatureEntry409 static bool performAction (const Entry<EntryData> *entry) 410 { return entry->flags & Offset; } 411 ligActionIndexAAT::LigatureEntry412 static unsigned int ligActionIndex (const Entry<EntryData> *entry) 413 { return entry->flags & Offset; } 414 }; 415 416 417 template <typename Types> 418 struct LigatureSubtable 419 { 420 typedef typename Types::HBUINT HBUINT; 421 422 typedef LigatureEntry<Types::extended> LigatureEntryT; 423 typedef typename LigatureEntryT::EntryData EntryData; 424 425 struct driver_context_t 426 { 427 enum { in_place = false }; 428 enum 429 { 430 DontAdvance = LigatureEntryT::DontAdvance, 431 }; 432 enum LigActionFlags 433 { 434 LigActionLast = 0x80000000, /* This is the last action in the list. This also 435 * implies storage. */ 436 LigActionStore = 0x40000000, /* Store the ligature at the current cumulated index 437 * in the ligature table in place of the marked 438 * (i.e. currently-popped) glyph. */ 439 LigActionOffset = 0x3FFFFFFF, /* A 30-bit value which is sign-extended to 32-bits 440 * and added to the glyph ID, resulting in an index 441 * into the component table. */ 442 }; 443 driver_context_tAAT::LigatureSubtable::driver_context_t444 driver_context_t (const LigatureSubtable *table_, 445 hb_aat_apply_context_t *c_) : 446 ret (false), 447 c (c_), 448 table (table_), 449 ligAction (table+table->ligAction), 450 component (table+table->component), 451 ligature (table+table->ligature), 452 match_length (0) {} 453 is_actionableAAT::LigatureSubtable::driver_context_t454 bool is_actionable (StateTableDriver<Types, EntryData> *driver HB_UNUSED, 455 const Entry<EntryData> *entry) 456 { 457 return LigatureEntryT::performAction (entry); 458 } transitionAAT::LigatureSubtable::driver_context_t459 bool transition (StateTableDriver<Types, EntryData> *driver, 460 const Entry<EntryData> *entry) 461 { 462 hb_buffer_t *buffer = driver->buffer; 463 464 DEBUG_MSG (APPLY, nullptr, "Ligature transition at %u", buffer->idx); 465 if (entry->flags & LigatureEntryT::SetComponent) 466 { 467 if (unlikely (match_length >= ARRAY_LENGTH (match_positions))) 468 return false; 469 470 /* Never mark same index twice, in case DontAdvance was used... */ 471 if (match_length && match_positions[match_length - 1] == buffer->out_len) 472 match_length--; 473 474 match_positions[match_length++] = buffer->out_len; 475 DEBUG_MSG (APPLY, nullptr, "Set component at %u", buffer->out_len); 476 } 477 478 if (LigatureEntryT::performAction (entry)) 479 { 480 DEBUG_MSG (APPLY, nullptr, "Perform action with %u", match_length); 481 unsigned int end = buffer->out_len; 482 483 if (unlikely (!match_length)) 484 return true; 485 486 if (buffer->idx >= buffer->len) 487 return false; // TODO Work on previous instead? 488 489 unsigned int cursor = match_length; 490 491 unsigned int action_idx = LigatureEntryT::ligActionIndex (entry); 492 action_idx = Types::offsetToIndex (action_idx, table, ligAction.arrayZ); 493 const HBUINT32 *actionData = &ligAction[action_idx]; 494 495 unsigned int ligature_idx = 0; 496 unsigned int action; 497 do 498 { 499 if (unlikely (!cursor)) 500 { 501 /* Stack underflow. Clear the stack. */ 502 DEBUG_MSG (APPLY, nullptr, "Stack underflow"); 503 match_length = 0; 504 break; 505 } 506 507 DEBUG_MSG (APPLY, nullptr, "Moving to stack position %u", cursor - 1); 508 buffer->move_to (match_positions[--cursor]); 509 510 if (unlikely (!actionData->sanitize (&c->sanitizer))) return false; 511 action = *actionData; 512 513 uint32_t uoffset = action & LigActionOffset; 514 if (uoffset & 0x20000000) 515 uoffset |= 0xC0000000; /* Sign-extend. */ 516 int32_t offset = (int32_t) uoffset; 517 unsigned int component_idx = buffer->cur().codepoint + offset; 518 component_idx = Types::wordOffsetToIndex (component_idx, table, component.arrayZ); 519 const HBUINT16 &componentData = component[component_idx]; 520 if (unlikely (!componentData.sanitize (&c->sanitizer))) return false; 521 ligature_idx += componentData; 522 523 DEBUG_MSG (APPLY, nullptr, "Action store %u last %u", 524 bool (action & LigActionStore), 525 bool (action & LigActionLast)); 526 if (action & (LigActionStore | LigActionLast)) 527 { 528 ligature_idx = Types::offsetToIndex (ligature_idx, table, ligature.arrayZ); 529 const GlyphID &ligatureData = ligature[ligature_idx]; 530 if (unlikely (!ligatureData.sanitize (&c->sanitizer))) return false; 531 hb_codepoint_t lig = ligatureData; 532 533 DEBUG_MSG (APPLY, nullptr, "Produced ligature %u", lig); 534 buffer->replace_glyph (lig); 535 536 unsigned int lig_end = match_positions[match_length - 1] + 1; 537 /* Now go and delete all subsequent components. */ 538 while (match_length - 1 > cursor) 539 { 540 DEBUG_MSG (APPLY, nullptr, "Skipping ligature component"); 541 buffer->move_to (match_positions[--match_length]); 542 buffer->replace_glyph (DELETED_GLYPH); 543 } 544 545 buffer->move_to (lig_end); 546 buffer->merge_out_clusters (match_positions[cursor], buffer->out_len); 547 } 548 549 actionData++; 550 } 551 while (!(action & LigActionLast)); 552 buffer->move_to (end); 553 } 554 555 return true; 556 } 557 558 public: 559 bool ret; 560 private: 561 hb_aat_apply_context_t *c; 562 const LigatureSubtable *table; 563 const UnsizedArrayOf<HBUINT32> &ligAction; 564 const UnsizedArrayOf<HBUINT16> &component; 565 const UnsizedArrayOf<GlyphID> &ligature; 566 unsigned int match_length; 567 unsigned int match_positions[HB_MAX_CONTEXT_LENGTH]; 568 }; 569 applyAAT::LigatureSubtable570 bool apply (hb_aat_apply_context_t *c) const 571 { 572 TRACE_APPLY (this); 573 574 driver_context_t dc (this, c); 575 576 StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->face); 577 driver.drive (&dc); 578 579 return_trace (dc.ret); 580 } 581 sanitizeAAT::LigatureSubtable582 bool sanitize (hb_sanitize_context_t *c) const 583 { 584 TRACE_SANITIZE (this); 585 /* The rest of array sanitizations are done at run-time. */ 586 return_trace (c->check_struct (this) && machine.sanitize (c) && 587 ligAction && component && ligature); 588 } 589 590 protected: 591 StateTable<Types, EntryData> 592 machine; 593 OffsetTo<UnsizedArrayOf<HBUINT32>, HBUINT, false> 594 ligAction; /* Offset to the ligature action table. */ 595 OffsetTo<UnsizedArrayOf<HBUINT16>, HBUINT, false> 596 component; /* Offset to the component table. */ 597 OffsetTo<UnsizedArrayOf<GlyphID>, HBUINT, false> 598 ligature; /* Offset to the actual ligature lists. */ 599 public: 600 DEFINE_SIZE_STATIC (28); 601 }; 602 603 template <typename Types> 604 struct NoncontextualSubtable 605 { applyAAT::NoncontextualSubtable606 bool apply (hb_aat_apply_context_t *c) const 607 { 608 TRACE_APPLY (this); 609 610 bool ret = false; 611 unsigned int num_glyphs = c->face->get_num_glyphs (); 612 613 hb_glyph_info_t *info = c->buffer->info; 614 unsigned int count = c->buffer->len; 615 for (unsigned int i = 0; i < count; i++) 616 { 617 const GlyphID *replacement = substitute.get_value (info[i].codepoint, num_glyphs); 618 if (replacement) 619 { 620 info[i].codepoint = *replacement; 621 ret = true; 622 } 623 } 624 625 return_trace (ret); 626 } 627 sanitizeAAT::NoncontextualSubtable628 bool sanitize (hb_sanitize_context_t *c) const 629 { 630 TRACE_SANITIZE (this); 631 return_trace (substitute.sanitize (c)); 632 } 633 634 protected: 635 Lookup<GlyphID> substitute; 636 public: 637 DEFINE_SIZE_MIN (2); 638 }; 639 640 template <typename Types> 641 struct InsertionSubtable 642 { 643 typedef typename Types::HBUINT HBUINT; 644 645 struct EntryData 646 { 647 HBUINT16 currentInsertIndex; /* Zero-based index into the insertion glyph table. 648 * The number of glyphs to be inserted is contained 649 * in the currentInsertCount field in the flags. 650 * A value of 0xFFFF indicates no insertion is to 651 * be done. */ 652 HBUINT16 markedInsertIndex; /* Zero-based index into the insertion glyph table. 653 * The number of glyphs to be inserted is contained 654 * in the markedInsertCount field in the flags. 655 * A value of 0xFFFF indicates no insertion is to 656 * be done. */ 657 public: 658 DEFINE_SIZE_STATIC (4); 659 }; 660 661 struct driver_context_t 662 { 663 enum { in_place = false }; 664 enum Flags 665 { 666 SetMark = 0x8000, /* If set, mark the current glyph. */ 667 DontAdvance = 0x4000, /* If set, don't advance to the next glyph before 668 * going to the new state. This does not mean 669 * that the glyph pointed to is the same one as 670 * before. If you've made insertions immediately 671 * downstream of the current glyph, the next glyph 672 * processed would in fact be the first one 673 * inserted. */ 674 CurrentIsKashidaLike= 0x2000, /* If set, and the currentInsertList is nonzero, 675 * then the specified glyph list will be inserted 676 * as a kashida-like insertion, either before or 677 * after the current glyph (depending on the state 678 * of the currentInsertBefore flag). If clear, and 679 * the currentInsertList is nonzero, then the 680 * specified glyph list will be inserted as a 681 * split-vowel-like insertion, either before or 682 * after the current glyph (depending on the state 683 * of the currentInsertBefore flag). */ 684 MarkedIsKashidaLike= 0x1000, /* If set, and the markedInsertList is nonzero, 685 * then the specified glyph list will be inserted 686 * as a kashida-like insertion, either before or 687 * after the marked glyph (depending on the state 688 * of the markedInsertBefore flag). If clear, and 689 * the markedInsertList is nonzero, then the 690 * specified glyph list will be inserted as a 691 * split-vowel-like insertion, either before or 692 * after the marked glyph (depending on the state 693 * of the markedInsertBefore flag). */ 694 CurrentInsertBefore= 0x0800, /* If set, specifies that insertions are to be made 695 * to the left of the current glyph. If clear, 696 * they're made to the right of the current glyph. */ 697 MarkedInsertBefore= 0x0400, /* If set, specifies that insertions are to be 698 * made to the left of the marked glyph. If clear, 699 * they're made to the right of the marked glyph. */ 700 CurrentInsertCount= 0x3E0, /* This 5-bit field is treated as a count of the 701 * number of glyphs to insert at the current 702 * position. Since zero means no insertions, the 703 * largest number of insertions at any given 704 * current location is 31 glyphs. */ 705 MarkedInsertCount= 0x001F, /* This 5-bit field is treated as a count of the 706 * number of glyphs to insert at the marked 707 * position. Since zero means no insertions, the 708 * largest number of insertions at any given 709 * marked location is 31 glyphs. */ 710 }; 711 driver_context_tAAT::InsertionSubtable::driver_context_t712 driver_context_t (const InsertionSubtable *table, 713 hb_aat_apply_context_t *c_) : 714 ret (false), 715 c (c_), 716 mark_set (false), 717 mark (0), 718 insertionAction (table+table->insertionAction) {} 719 is_actionableAAT::InsertionSubtable::driver_context_t720 bool is_actionable (StateTableDriver<Types, EntryData> *driver HB_UNUSED, 721 const Entry<EntryData> *entry) 722 { 723 return (entry->flags & (CurrentInsertCount | MarkedInsertCount)) && 724 (entry->data.currentInsertIndex != 0xFFFF ||entry->data.markedInsertIndex != 0xFFFF); 725 } transitionAAT::InsertionSubtable::driver_context_t726 bool transition (StateTableDriver<Types, EntryData> *driver, 727 const Entry<EntryData> *entry) 728 { 729 hb_buffer_t *buffer = driver->buffer; 730 unsigned int flags = entry->flags; 731 732 if (entry->data.markedInsertIndex != 0xFFFF && mark_set) 733 { 734 unsigned int count = (flags & MarkedInsertCount); 735 unsigned int start = entry->data.markedInsertIndex; 736 const GlyphID *glyphs = &insertionAction[start]; 737 if (unlikely (!c->sanitizer.check_array (glyphs, count))) return false; 738 739 bool before = flags & MarkedInsertBefore; 740 741 unsigned int end = buffer->out_len; 742 buffer->move_to (mark); 743 744 if (buffer->idx < buffer->len && !before) 745 buffer->copy_glyph (); 746 /* TODO We ignore KashidaLike setting. */ 747 for (unsigned int i = 0; i < count; i++) 748 buffer->output_glyph (glyphs[i]); 749 if (buffer->idx < buffer->len && !before) 750 buffer->skip_glyph (); 751 752 buffer->move_to (end + count); 753 754 buffer->unsafe_to_break_from_outbuffer (mark, MIN (buffer->idx + 1, buffer->len)); 755 } 756 757 if (entry->data.currentInsertIndex != 0xFFFF) 758 { 759 unsigned int count = (flags & CurrentInsertCount) >> 5; 760 unsigned int start = entry->data.currentInsertIndex; 761 const GlyphID *glyphs = &insertionAction[start]; 762 if (unlikely (!c->sanitizer.check_array (glyphs, count))) return false; 763 764 bool before = flags & CurrentInsertBefore; 765 766 unsigned int end = buffer->out_len; 767 768 if (buffer->idx < buffer->len && !before) 769 buffer->copy_glyph (); 770 /* TODO We ignore KashidaLike setting. */ 771 for (unsigned int i = 0; i < count; i++) 772 buffer->output_glyph (glyphs[i]); 773 if (buffer->idx < buffer->len && !before) 774 buffer->skip_glyph (); 775 776 /* Humm. Not sure where to move to. There's this wording under 777 * DontAdvance flag: 778 * 779 * "If set, don't update the glyph index before going to the new state. 780 * This does not mean that the glyph pointed to is the same one as 781 * before. If you've made insertions immediately downstream of the 782 * current glyph, the next glyph processed would in fact be the first 783 * one inserted." 784 * 785 * This suggests that if DontAdvance is NOT set, we should move to 786 * end+count. If it *was*, then move to end, such that newly inserted 787 * glyphs are now visible. 788 * 789 * https://github.com/harfbuzz/harfbuzz/issues/1224#issuecomment-427691417 790 */ 791 buffer->move_to ((flags & DontAdvance) ? end : end + count); 792 } 793 794 if (flags & SetMark) 795 { 796 mark_set = true; 797 mark = buffer->out_len; 798 } 799 800 return true; 801 } 802 803 public: 804 bool ret; 805 private: 806 hb_aat_apply_context_t *c; 807 bool mark_set; 808 unsigned int mark; 809 const UnsizedArrayOf<GlyphID> &insertionAction; 810 }; 811 applyAAT::InsertionSubtable812 bool apply (hb_aat_apply_context_t *c) const 813 { 814 TRACE_APPLY (this); 815 816 driver_context_t dc (this, c); 817 818 StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->face); 819 driver.drive (&dc); 820 821 return_trace (dc.ret); 822 } 823 sanitizeAAT::InsertionSubtable824 bool sanitize (hb_sanitize_context_t *c) const 825 { 826 TRACE_SANITIZE (this); 827 /* The rest of array sanitizations are done at run-time. */ 828 return_trace (c->check_struct (this) && machine.sanitize (c) && 829 insertionAction); 830 } 831 832 protected: 833 StateTable<Types, EntryData> 834 machine; 835 OffsetTo<UnsizedArrayOf<GlyphID>, HBUINT, false> 836 insertionAction; /* Byte offset from stateHeader to the start of 837 * the insertion glyph table. */ 838 public: 839 DEFINE_SIZE_STATIC (20); 840 }; 841 842 843 struct Feature 844 { sanitizeAAT::Feature845 bool sanitize (hb_sanitize_context_t *c) const 846 { 847 TRACE_SANITIZE (this); 848 return_trace (c->check_struct (this)); 849 } 850 851 public: 852 HBUINT16 featureType; /* The type of feature. */ 853 HBUINT16 featureSetting; /* The feature's setting (aka selector). */ 854 HBUINT32 enableFlags; /* Flags for the settings that this feature 855 * and setting enables. */ 856 HBUINT32 disableFlags; /* Complement of flags for the settings that this 857 * feature and setting disable. */ 858 859 public: 860 DEFINE_SIZE_STATIC (12); 861 }; 862 863 template <typename Types> 864 struct ChainSubtable 865 { 866 typedef typename Types::HBUINT HBUINT; 867 868 template <typename T> 869 friend struct Chain; 870 get_sizeAAT::ChainSubtable871 unsigned int get_size () const { return length; } get_typeAAT::ChainSubtable872 unsigned int get_type () const { return coverage & 0xFF; } get_coverageAAT::ChainSubtable873 unsigned int get_coverage () const { return coverage >> (sizeof (HBUINT) * 8 - 8); } 874 875 enum Coverage 876 { 877 Vertical = 0x80, /* If set, this subtable will only be applied 878 * to vertical text. If clear, this subtable 879 * will only be applied to horizontal text. */ 880 Backwards = 0x40, /* If set, this subtable will process glyphs 881 * in descending order. If clear, it will 882 * process the glyphs in ascending order. */ 883 AllDirections = 0x20, /* If set, this subtable will be applied to 884 * both horizontal and vertical text (i.e. 885 * the state of bit 0x80000000 is ignored). */ 886 Logical = 0x10, /* If set, this subtable will process glyphs 887 * in logical order (or reverse logical order, 888 * depending on the value of bit 0x80000000). */ 889 }; 890 enum Type 891 { 892 Rearrangement = 0, 893 Contextual = 1, 894 Ligature = 2, 895 Noncontextual = 4, 896 Insertion = 5 897 }; 898 899 template <typename context_t> dispatchAAT::ChainSubtable900 typename context_t::return_t dispatch (context_t *c) const 901 { 902 unsigned int subtable_type = get_type (); 903 TRACE_DISPATCH (this, subtable_type); 904 switch (subtable_type) { 905 case Rearrangement: return_trace (c->dispatch (u.rearrangement)); 906 case Contextual: return_trace (c->dispatch (u.contextual)); 907 case Ligature: return_trace (c->dispatch (u.ligature)); 908 case Noncontextual: return_trace (c->dispatch (u.noncontextual)); 909 case Insertion: return_trace (c->dispatch (u.insertion)); 910 default: return_trace (c->default_return_value ()); 911 } 912 } 913 applyAAT::ChainSubtable914 bool apply (hb_aat_apply_context_t *c) const 915 { 916 TRACE_APPLY (this); 917 hb_sanitize_with_object_t with (&c->sanitizer, this); 918 return_trace (dispatch (c)); 919 } 920 sanitizeAAT::ChainSubtable921 bool sanitize (hb_sanitize_context_t *c) const 922 { 923 TRACE_SANITIZE (this); 924 if (!length.sanitize (c) || 925 length <= min_size || 926 !c->check_range (this, length)) 927 return_trace (false); 928 929 hb_sanitize_with_object_t with (c, this); 930 return_trace (dispatch (c)); 931 } 932 933 protected: 934 HBUINT length; /* Total subtable length, including this header. */ 935 HBUINT coverage; /* Coverage flags and subtable type. */ 936 HBUINT32 subFeatureFlags;/* The 32-bit mask identifying which subtable this is. */ 937 union { 938 RearrangementSubtable<Types> rearrangement; 939 ContextualSubtable<Types> contextual; 940 LigatureSubtable<Types> ligature; 941 NoncontextualSubtable<Types> noncontextual; 942 InsertionSubtable<Types> insertion; 943 } u; 944 public: 945 DEFINE_SIZE_MIN (2 * sizeof (HBUINT) + 4); 946 }; 947 948 template <typename Types> 949 struct Chain 950 { 951 typedef typename Types::HBUINT HBUINT; 952 compile_flagsAAT::Chain953 hb_mask_t compile_flags (const hb_aat_map_builder_t *map) const 954 { 955 hb_mask_t flags = defaultFlags; 956 { 957 unsigned int count = featureCount; 958 for (unsigned i = 0; i < count; i++) 959 { 960 const Feature &feature = featureZ[i]; 961 hb_aat_layout_feature_type_t type = (hb_aat_layout_feature_type_t) (unsigned int) feature.featureType; 962 hb_aat_layout_feature_selector_t setting = (hb_aat_layout_feature_selector_t) (unsigned int) feature.featureSetting; 963 retry: 964 const hb_aat_map_builder_t::feature_info_t *info = map->features.bsearch ((uint16_t) type); 965 if (info && info->setting == setting) 966 { 967 flags &= feature.disableFlags; 968 flags |= feature.enableFlags; 969 } 970 else if (type == HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE && setting == HB_AAT_LAYOUT_FEATURE_SELECTOR_SMALL_CAPS) 971 { 972 /* Deprecated. https://github.com/harfbuzz/harfbuzz/issues/1342 */ 973 type = HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE; 974 setting = HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS; 975 goto retry; 976 } 977 } 978 } 979 return flags; 980 } 981 applyAAT::Chain982 void apply (hb_aat_apply_context_t *c, 983 hb_mask_t flags) const 984 { 985 const ChainSubtable<Types> *subtable = &StructAfter<ChainSubtable<Types> > (featureZ.as_array (featureCount)); 986 unsigned int count = subtableCount; 987 for (unsigned int i = 0; i < count; i++) 988 { 989 bool reverse; 990 991 if (!(subtable->subFeatureFlags & flags)) 992 goto skip; 993 994 if (!(subtable->get_coverage() & ChainSubtable<Types>::AllDirections) && 995 HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) != 996 bool (subtable->get_coverage() & ChainSubtable<Types>::Vertical)) 997 goto skip; 998 999 /* Buffer contents is always in logical direction. Determine if 1000 * we need to reverse before applying this subtable. We reverse 1001 * back after if we did reverse indeed. 1002 * 1003 * Quoting the spac: 1004 * """ 1005 * Bits 28 and 30 of the coverage field control the order in which 1006 * glyphs are processed when the subtable is run by the layout engine. 1007 * Bit 28 is used to indicate if the glyph processing direction is 1008 * the same as logical order or layout order. Bit 30 is used to 1009 * indicate whether glyphs are processed forwards or backwards within 1010 * that order. 1011 1012 Bit 30 Bit 28 Interpretation for Horizontal Text 1013 0 0 The subtable is processed in layout order 1014 (the same order as the glyphs, which is 1015 always left-to-right). 1016 1 0 The subtable is processed in reverse layout order 1017 (the order opposite that of the glyphs, which is 1018 always right-to-left). 1019 0 1 The subtable is processed in logical order 1020 (the same order as the characters, which may be 1021 left-to-right or right-to-left). 1022 1 1 The subtable is processed in reverse logical order 1023 (the order opposite that of the characters, which 1024 may be right-to-left or left-to-right). 1025 */ 1026 reverse = subtable->get_coverage () & ChainSubtable<Types>::Logical ? 1027 bool (subtable->get_coverage () & ChainSubtable<Types>::Backwards) : 1028 bool (subtable->get_coverage () & ChainSubtable<Types>::Backwards) != 1029 HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction); 1030 1031 if (!c->buffer->message (c->font, "start chain subtable %d", c->lookup_index)) 1032 goto skip; 1033 1034 if (reverse) 1035 c->buffer->reverse (); 1036 1037 subtable->apply (c); 1038 1039 if (reverse) 1040 c->buffer->reverse (); 1041 1042 (void) c->buffer->message (c->font, "end chain subtable %d", c->lookup_index); 1043 1044 if (unlikely (!c->buffer->successful)) return; 1045 1046 skip: 1047 subtable = &StructAfter<ChainSubtable<Types> > (*subtable); 1048 c->set_lookup_index (c->lookup_index + 1); 1049 } 1050 } 1051 get_sizeAAT::Chain1052 unsigned int get_size () const { return length; } 1053 sanitizeAAT::Chain1054 bool sanitize (hb_sanitize_context_t *c, unsigned int version HB_UNUSED) const 1055 { 1056 TRACE_SANITIZE (this); 1057 if (!length.sanitize (c) || 1058 length < min_size || 1059 !c->check_range (this, length)) 1060 return_trace (false); 1061 1062 if (!c->check_array (featureZ.arrayZ, featureCount)) 1063 return_trace (false); 1064 1065 const ChainSubtable<Types> *subtable = &StructAfter<ChainSubtable<Types> > (featureZ.as_array (featureCount)); 1066 unsigned int count = subtableCount; 1067 for (unsigned int i = 0; i < count; i++) 1068 { 1069 if (!subtable->sanitize (c)) 1070 return_trace (false); 1071 subtable = &StructAfter<ChainSubtable<Types> > (*subtable); 1072 } 1073 1074 return_trace (true); 1075 } 1076 1077 protected: 1078 HBUINT32 defaultFlags; /* The default specification for subtables. */ 1079 HBUINT32 length; /* Total byte count, including this header. */ 1080 HBUINT featureCount; /* Number of feature subtable entries. */ 1081 HBUINT subtableCount; /* The number of subtables in the chain. */ 1082 1083 UnsizedArrayOf<Feature> featureZ; /* Features. */ 1084 /*ChainSubtable firstSubtable;*//* Subtables. */ 1085 /*subtableGlyphCoverageArray*/ /* Only if version >= 3. We don't use. */ 1086 1087 public: 1088 DEFINE_SIZE_MIN (8 + 2 * sizeof (HBUINT)); 1089 }; 1090 1091 1092 /* 1093 * The 'mort'/'morx' Table 1094 */ 1095 1096 template <typename Types> 1097 struct mortmorx 1098 { 1099 enum { tableTag = HB_AAT_TAG_morx }; 1100 has_dataAAT::mortmorx1101 bool has_data () const { return version != 0; } 1102 compile_flagsAAT::mortmorx1103 void compile_flags (const hb_aat_map_builder_t *mapper, 1104 hb_aat_map_t *map) const 1105 { 1106 const Chain<Types> *chain = &firstChain; 1107 unsigned int count = chainCount; 1108 for (unsigned int i = 0; i < count; i++) 1109 { 1110 map->chain_flags.push (chain->compile_flags (mapper)); 1111 chain = &StructAfter<Chain<Types> > (*chain); 1112 } 1113 } 1114 applyAAT::mortmorx1115 void apply (hb_aat_apply_context_t *c) const 1116 { 1117 if (unlikely (!c->buffer->successful)) return; 1118 c->set_lookup_index (0); 1119 const Chain<Types> *chain = &firstChain; 1120 unsigned int count = chainCount; 1121 for (unsigned int i = 0; i < count; i++) 1122 { 1123 chain->apply (c, c->plan->aat_map.chain_flags[i]); 1124 if (unlikely (!c->buffer->successful)) return; 1125 chain = &StructAfter<Chain<Types> > (*chain); 1126 } 1127 } 1128 sanitizeAAT::mortmorx1129 bool sanitize (hb_sanitize_context_t *c) const 1130 { 1131 TRACE_SANITIZE (this); 1132 if (!version.sanitize (c) || !version || !chainCount.sanitize (c)) 1133 return_trace (false); 1134 1135 const Chain<Types> *chain = &firstChain; 1136 unsigned int count = chainCount; 1137 for (unsigned int i = 0; i < count; i++) 1138 { 1139 if (!chain->sanitize (c, version)) 1140 return_trace (false); 1141 chain = &StructAfter<Chain<Types> > (*chain); 1142 } 1143 1144 return_trace (true); 1145 } 1146 1147 protected: 1148 HBUINT16 version; /* Version number of the glyph metamorphosis table. 1149 * 1, 2, or 3. */ 1150 HBUINT16 unused; /* Set to 0. */ 1151 HBUINT32 chainCount; /* Number of metamorphosis chains contained in this 1152 * table. */ 1153 Chain<Types> firstChain; /* Chains. */ 1154 1155 public: 1156 DEFINE_SIZE_MIN (8); 1157 }; 1158 1159 struct morx : mortmorx<ExtendedTypes> 1160 { 1161 enum { tableTag = HB_AAT_TAG_morx }; 1162 }; 1163 struct mort : mortmorx<ObsoleteTypes> 1164 { 1165 enum { tableTag = HB_AAT_TAG_mort }; 1166 }; 1167 1168 1169 } /* namespace AAT */ 1170 1171 1172 #endif /* HB_AAT_LAYOUT_MORX_TABLE_HH */ 1173