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