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