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
34/*
35 * morx -- Extended Glyph Metamorphosis
36 * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html
37 */
38#define HB_AAT_TAG_morx HB_TAG('m','o','r','x')
39
40
41namespace AAT {
42
43using namespace OT;
44
45
46struct RearrangementSubtable
47{
48 typedef void EntryData;
49
50 struct driver_context_t
51 {
52 static const bool in_place = true;
53 enum Flags {
54 MarkFirst = 0x8000, /* If set, make the current glyph the first
55 * glyph to be rearranged. */
56 DontAdvance = 0x4000, /* If set, don't advance to the next glyph
57 * before going to the new state. This means
58 * that the glyph index doesn't change, even
59 * if the glyph at that index has changed. */
60 MarkLast = 0x2000, /* If set, make the current glyph the last
61 * glyph to be rearranged. */
62 Reserved = 0x1FF0, /* These bits are reserved and should be set to 0. */
63 Verb = 0x000F, /* The type of rearrangement specified. */
64 };
65
66 inline driver_context_t (const RearrangementSubtable *table) :
67 ret (false),
68 start (0), end (0) {}
69
70 inline bool is_actionable (StateTableDriver<EntryData> *driver,
71 const Entry<EntryData> *entry)
72 {
73 return (entry->flags & Verb) && start < end;
74 }
75 inline bool transition (StateTableDriver<EntryData> *driver,
76 const Entry<EntryData> *entry)
77 {
78 hb_buffer_t *buffer = driver->buffer;
79 unsigned int flags = entry->flags;
80
81 if (flags & MarkFirst)
82 start = buffer->idx;
83
84 if (flags & MarkLast)
85 end = MIN (buffer->idx + 1, buffer->len);
86
87 if ((flags & Verb) && start < end)
88 {
89 /* The following map has two nibbles, for start-side
90 * and end-side. Values of 0,1,2 mean move that many
91 * to the other side. Value of 3 means move 2 and
92 * flip them. */
93 const unsigned char map[16] =
94 {
95 0x00, /* 0 no change */
96 0x10, /* 1 Ax => xA */
97 0x01, /* 2 xD => Dx */
98 0x11, /* 3 AxD => DxA */
99 0x20, /* 4 ABx => xAB */
100 0x30, /* 5 ABx => xBA */
101 0x02, /* 6 xCD => CDx */
102 0x03, /* 7 xCD => DCx */
103 0x12, /* 8 AxCD => CDxA */
104 0x13, /* 9 AxCD => DCxA */
105 0x21, /* 10 ABxD => DxAB */
106 0x31, /* 11 ABxD => DxBA */
107 0x22, /* 12 ABxCD => CDxAB */
108 0x32, /* 13 ABxCD => CDxBA */
109 0x23, /* 14 ABxCD => DCxAB */
110 0x33, /* 15 ABxCD => DCxBA */
111 };
112
113 unsigned int m = map[flags & Verb];
114 unsigned int l = MIN<unsigned int> (2, m >> 4);
115 unsigned int r = MIN<unsigned int> (2, m & 0x0F);
116 bool reverse_l = 3 == (m >> 4);
117 bool reverse_r = 3 == (m & 0x0F);
118
119 if (end - start >= l + r)
120 {
121 buffer->merge_clusters (start, MIN (buffer->idx + 1, buffer->len));
122 buffer->merge_clusters (start, end);
123
124 hb_glyph_info_t *info = buffer->info;
125 hb_glyph_info_t buf[4];
126
127 memcpy (buf, info + start, l * sizeof (buf[0]));
128 memcpy (buf + 2, info + end - r, r * sizeof (buf[0]));
129
130 if (l != r)
131 memmove (info + start + r, info + start + l, (end - start - l - r) * sizeof (buf[0]));
132
133 memcpy (info + start, buf + 2, r * sizeof (buf[0]));
134 memcpy (info + end - l, buf, l * sizeof (buf[0]));
135 if (reverse_l)
136 {
137 buf[0] = info[end - 1];
138 info[end - 1] = info[end - 2];
139 info[end - 2] = buf[0];
140 }
141 if (reverse_r)
142 {
143 buf[0] = info[start];
144 info[start] = info[start + 1];
145 info[start + 1] = buf[0];
146 }
147 }
148 }
149
150 return true;
151 }
152
153 public:
154 bool ret;
155 private:
156 unsigned int start;
157 unsigned int end;
158 };
159
160 inline bool apply (hb_aat_apply_context_t *c) const
161 {
162 TRACE_APPLY (this);
163
164 driver_context_t dc (this);
165
166 StateTableDriver<void> driver (machine, c->buffer, c->face);
167 driver.drive (&dc);
168
169 return_trace (dc.ret);
170 }
171
172 inline bool sanitize (hb_sanitize_context_t *c) const
173 {
174 TRACE_SANITIZE (this);
175 return_trace (machine.sanitize (c));
176 }
177
178 protected:
179 StateTable<EntryData> machine;
180 public:
181 DEFINE_SIZE_STATIC (16);
182};
183
184struct ContextualSubtable
185{
186 struct EntryData
187 {
188 HBUINT16 markIndex; /* Index of the substitution table for the
189 * marked glyph (use 0xFFFF for none). */
190 HBUINT16 currentIndex; /* Index of the substitution table for the
191 * current glyph (use 0xFFFF for none). */
192 public:
193 DEFINE_SIZE_STATIC (4);
194 };
195
196 struct driver_context_t
197 {
198 static const bool in_place = true;
199 enum Flags {
200 SetMark = 0x8000, /* If set, make the current glyph the marked glyph. */
201 DontAdvance = 0x4000, /* If set, don't advance to the next glyph before
202 * going to the new state. */
203 Reserved = 0x3FFF, /* These bits are reserved and should be set to 0. */
204 };
205
206 inline driver_context_t (const ContextualSubtable *table) :
207 ret (false),
208 mark_set (false),
209 mark (0),
210 subs (table+table->substitutionTables) {}
211
212 inline bool is_actionable (StateTableDriver<EntryData> *driver,
213 const Entry<EntryData> *entry)
214 {
215 hb_buffer_t *buffer = driver->buffer;
216
217 if (buffer->idx == buffer->len && !mark_set)
218 return false;
219
220 return entry->data.markIndex != 0xFFFF || entry->data.currentIndex != 0xFFFF;
221 }
222 inline bool transition (StateTableDriver<EntryData> *driver,
223 const Entry<EntryData> *entry)
224 {
225 hb_buffer_t *buffer = driver->buffer;
226
227 /* Looks like CoreText applies neither mark nor current substitution for
228 * end-of-text if mark was not explicitly set. */
229 if (buffer->idx == buffer->len && !mark_set)
230 return true;
231
232 if (entry->data.markIndex != 0xFFFF)
233 {
234 const Lookup<GlyphID> &lookup = subs[entry->data.markIndex];
235 hb_glyph_info_t *info = buffer->info;
236 const GlyphID *replacement = lookup.get_value (info[mark].codepoint, driver->num_glyphs);
237 if (replacement)
238 {
239 buffer->unsafe_to_break (mark, MIN (buffer->idx + 1, buffer->len));
240 info[mark].codepoint = *replacement;
241 ret = true;
242 }
243 }
244 if (entry->data.currentIndex != 0xFFFF)
245 {
246 unsigned int idx = MIN (buffer->idx, buffer->len - 1);
247 const Lookup<GlyphID> &lookup = subs[entry->data.currentIndex];
248 hb_glyph_info_t *info = buffer->info;
249 const GlyphID *replacement = lookup.get_value (info[idx].codepoint, driver->num_glyphs);
250 if (replacement)
251 {
252 info[idx].codepoint = *replacement;
253 ret = true;
254 }
255 }
256
257 if (entry->flags & SetMark)
258 {
259 mark_set = true;
260 mark = buffer->idx;
261 }
262
263 return true;
264 }
265
266 public:
267 bool ret;
268 private:
269 bool mark_set;
270 unsigned int mark;
271 const UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT32> &subs;
272 };
273
274 inline bool apply (hb_aat_apply_context_t *c) const
275 {
276 TRACE_APPLY (this);
277
278 driver_context_t dc (this);
279
280 StateTableDriver<EntryData> driver (machine, c->buffer, c->face);
281 driver.drive (&dc);
282
283 return_trace (dc.ret);
284 }
285
286 inline bool sanitize (hb_sanitize_context_t *c) const
287 {
288 TRACE_SANITIZE (this);
289
290 unsigned int num_entries = 0;
291 if (unlikely (!machine.sanitize (c, &num_entries))) return_trace (false);
292
293 unsigned int num_lookups = 0;
294
295 const Entry<EntryData> *entries = machine.get_entries ();
296 for (unsigned int i = 0; i < num_entries; i++)
297 {
298 const EntryData &data = entries[i].data;
299
300 if (data.markIndex != 0xFFFF)
301 num_lookups = MAX<unsigned int> (num_lookups, 1 + data.markIndex);
302 if (data.currentIndex != 0xFFFF)
303 num_lookups = MAX<unsigned int> (num_lookups, 1 + data.currentIndex);
304 }
305
306 return_trace (substitutionTables.sanitize (c, this, num_lookups));
307 }
308
309 protected:
310 StateTable<EntryData>
311 machine;
312 LOffsetTo<UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT32> >
313 substitutionTables;
314 public:
315 DEFINE_SIZE_STATIC (20);
316};
317
318struct LigatureSubtable
319{
320 struct EntryData
321 {
322 HBUINT16 ligActionIndex; /* Index to the first ligActionTable entry
323 * for processing this group, if indicated
324 * by the flags. */
325 public:
326 DEFINE_SIZE_STATIC (2);
327 };
328
329 struct driver_context_t
330 {
331 static const bool in_place = false;
332 enum Flags {
333 SetComponent = 0x8000, /* Push this glyph onto the component stack for
334 * eventual processing. */
335 DontAdvance = 0x4000, /* Leave the glyph pointer at this glyph for the
336 next iteration. */
337 PerformAction = 0x2000, /* Use the ligActionIndex to process a ligature
338 * group. */
339 Reserved = 0x1FFF, /* These bits are reserved and should be set to 0. */
340 };
341 enum LigActionFlags {
342 LigActionLast = 0x80000000, /* This is the last action in the list. This also
343 * implies storage. */
344 LigActionStore = 0x40000000, /* Store the ligature at the current cumulated index
345 * in the ligature table in place of the marked
346 * (i.e. currently-popped) glyph. */
347 LigActionOffset = 0x3FFFFFFF, /* A 30-bit value which is sign-extended to 32-bits
348 * and added to the glyph ID, resulting in an index
349 * into the component table. */
350 };
351
352 inline driver_context_t (const LigatureSubtable *table,
353 hb_aat_apply_context_t *c_) :
354 ret (false),
355 c (c_),
356 ligAction (table+table->ligAction),
357 component (table+table->component),
358 ligature (table+table->ligature),
359 match_length (0) {}
360
361 inline bool is_actionable (StateTableDriver<EntryData> *driver,
362 const Entry<EntryData> *entry)
363 {
364 return !!(entry->flags & PerformAction);
365 }
366 inline bool transition (StateTableDriver<EntryData> *driver,
367 const Entry<EntryData> *entry)
368 {
369 hb_buffer_t *buffer = driver->buffer;
370 unsigned int flags = entry->flags;
371
372 if (flags & SetComponent)
373 {
374 if (unlikely (match_length >= ARRAY_LENGTH (match_positions)))
375 return false;
376
377 /* Never mark same index twice, in case DontAdvance was used... */
378 if (match_length && match_positions[match_length - 1] == buffer->out_len)
379 match_length--;
380
381 match_positions[match_length++] = buffer->out_len;
382 }
383
384 if (flags & PerformAction)
385 {
386 unsigned int end = buffer->out_len;
387 unsigned int action_idx = entry->data.ligActionIndex;
388 unsigned int action;
389 unsigned int ligature_idx = 0;
390 do
391 {
392 if (unlikely (!match_length))
393 return false;
394
395 buffer->move_to (match_positions[--match_length]);
396
397 const HBUINT32 &actionData = ligAction[action_idx];
398 if (unlikely (!actionData.sanitize (&c->sanitizer))) return false;
399 action = actionData;
400
401 uint32_t uoffset = action & LigActionOffset;
402 if (uoffset & 0x20000000)
403 uoffset += 0xC0000000;
404 int32_t offset = (int32_t) uoffset;
405 unsigned int component_idx = buffer->cur().codepoint + offset;
406
407 const HBUINT16 &componentData = component[component_idx];
408 if (unlikely (!componentData.sanitize (&c->sanitizer))) return false;
409 ligature_idx += componentData;
410
411 if (action & (LigActionStore | LigActionLast))
412 {
413 const GlyphID &ligatureData = ligature[ligature_idx];
414 if (unlikely (!ligatureData.sanitize (&c->sanitizer))) return false;
415 hb_codepoint_t lig = ligatureData;
416
417 match_positions[match_length++] = buffer->out_len;
418 buffer->replace_glyph (lig);
419
420 //ligature_idx = 0; // XXX Yes or no?
421 }
422 else
423 {
424 buffer->skip_glyph ();
425 end--;
426 }
427 /* TODO merge_clusters / unsafe_to_break */
428
429 action_idx++;
430 }
431 while (!(action & LigActionLast));
432 buffer->move_to (end);
433 }
434
435 return true;
436 }
437
438 public:
439 bool ret;
440 private:
441 hb_aat_apply_context_t *c;
442 const UnsizedArrayOf<HBUINT32> &ligAction;
443 const UnsizedArrayOf<HBUINT16> &component;
444 const UnsizedArrayOf<GlyphID> &ligature;
445 unsigned int match_length;
446 unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
447 };
448
449 inline bool apply (hb_aat_apply_context_t *c) const
450 {
451 TRACE_APPLY (this);
452
453 driver_context_t dc (this, c);
454
455 StateTableDriver<EntryData> driver (machine, c->buffer, c->face);
456 driver.drive (&dc);
457
458 return_trace (dc.ret);
459 }
460
461 inline bool sanitize (hb_sanitize_context_t *c) const
462 {
463 TRACE_SANITIZE (this);
464 /* The rest of array sanitizations are done at run-time. */
465 return_trace (c->check_struct (this) && machine.sanitize (c) &&
466 ligAction && component && ligature);
467 }
468
469 protected:
470 StateTable<EntryData>
471 machine;
472 LOffsetTo<UnsizedArrayOf<HBUINT32> >
473 ligAction; /* Offset to the ligature action table. */
474 LOffsetTo<UnsizedArrayOf<HBUINT16> >
475 component; /* Offset to the component table. */
476 LOffsetTo<UnsizedArrayOf<GlyphID> >
477 ligature; /* Offset to the actual ligature lists. */
478 public:
479 DEFINE_SIZE_STATIC (28);
480};
481
482struct NoncontextualSubtable
483{
484 inline bool apply (hb_aat_apply_context_t *c) const
485 {
486 TRACE_APPLY (this);
487
488 bool ret = false;
489 unsigned int num_glyphs = c->face->get_num_glyphs ();
490
491 hb_glyph_info_t *info = c->buffer->info;
492 unsigned int count = c->buffer->len;
493 for (unsigned int i = 0; i < count; i++)
494 {
495 const GlyphID *replacement = substitute.get_value (info[i].codepoint, num_glyphs);
496 if (replacement)
497 {
498 info[i].codepoint = *replacement;
499 ret = true;
500 }
501 }
502
503 return_trace (ret);
504 }
505
506 inline bool sanitize (hb_sanitize_context_t *c) const
507 {
508 TRACE_SANITIZE (this);
509 return_trace (substitute.sanitize (c));
510 }
511
512 protected:
513 Lookup<GlyphID> substitute;
514 public:
515 DEFINE_SIZE_MIN (2);
516};
517
518struct InsertionSubtable
519{
520 inline bool apply (hb_aat_apply_context_t *c) const
521 {
522 TRACE_APPLY (this);
523 /* TODO */
524 return_trace (false);
525 }
526
527 inline bool sanitize (hb_sanitize_context_t *c) const
528 {
529 TRACE_SANITIZE (this);
530 /* TODO */
531 return_trace (true);
532 }
533};
534
535
536struct Feature
537{
538 inline bool sanitize (hb_sanitize_context_t *c) const
539 {
540 TRACE_SANITIZE (this);
541 return_trace (c->check_struct (this));
542 }
543
544 public:
545 HBUINT16 featureType; /* The type of feature. */
546 HBUINT16 featureSetting; /* The feature's setting (aka selector). */
547 HBUINT32 enableFlags; /* Flags for the settings that this feature
548 * and setting enables. */
549 HBUINT32 disableFlags; /* Complement of flags for the settings that this
550 * feature and setting disable. */
551
552 public:
553 DEFINE_SIZE_STATIC (12);
554};
555
556
557struct ChainSubtable
558{
559 friend struct Chain;
560
561 inline unsigned int get_size (void) const { return length; }
562 inline unsigned int get_type (void) const { return coverage & 0xFF; }
563
564 enum Type {
565 Rearrangement = 0,
566 Contextual = 1,
567 Ligature = 2,
568 Noncontextual = 4,
569 Insertion = 5
570 };
571
572 inline void apply (hb_aat_apply_context_t *c) const
573 {
574 dispatch (c);
575 }
576
577 template <typename context_t>
578 inline typename context_t::return_t dispatch (context_t *c) const
579 {
580 unsigned int subtable_type = get_type ();
581 TRACE_DISPATCH (this, subtable_type);
582 switch (subtable_type) {
583 case Rearrangement: return_trace (c->dispatch (u.rearrangement));
584 case Contextual: return_trace (c->dispatch (u.contextual));
585 case Ligature: return_trace (c->dispatch (u.ligature));
586 case Noncontextual: return_trace (c->dispatch (u.noncontextual));
587 case Insertion: return_trace (c->dispatch (u.insertion));
588 default: return_trace (c->default_return_value ());
589 }
590 }
591
592 inline bool sanitize (hb_sanitize_context_t *c) const
593 {
594 TRACE_SANITIZE (this);
595 if (!length.sanitize (c) ||
596 length < min_size ||
597 !c->check_range (this, length))
598 return_trace (false);
599
600 return_trace (dispatch (c));
601 }
602
603 protected:
604 HBUINT32 length; /* Total subtable length, including this header. */
605 HBUINT32 coverage; /* Coverage flags and subtable type. */
606 HBUINT32 subFeatureFlags;/* The 32-bit mask identifying which subtable this is. */
607 union {
608 RearrangementSubtable rearrangement;
609 ContextualSubtable contextual;
610 LigatureSubtable ligature;
611 NoncontextualSubtable noncontextual;
612 InsertionSubtable insertion;
613 } u;
614 public:
615 DEFINE_SIZE_MIN (12);
616};
617
618struct Chain
619{
620 inline void apply (hb_aat_apply_context_t *c) const
621 {
622 const ChainSubtable *subtable = &StructAtOffset<ChainSubtable> (featureZ, featureZ[0].static_size * featureCount);
623 unsigned int count = subtableCount;
624 for (unsigned int i = 0; i < count; i++)
625 {
626 if (!c->buffer->message (c->font, "start chain subtable %d", c->lookup_index))
627 {
628 c->set_lookup_index (c->lookup_index + 1);
629 continue;
630 }
631
632 subtable->apply (c);
633 subtable = &StructAfter<ChainSubtable> (*subtable);
634
635 (void) c->buffer->message (c->font, "end chain subtable %d", c->lookup_index);
636
637 c->set_lookup_index (c->lookup_index + 1);
638 }
639 }
640
641 inline unsigned int get_size (void) const { return length; }
642
643 inline bool sanitize (hb_sanitize_context_t *c, unsigned int major) const
644 {
645 TRACE_SANITIZE (this);
646 if (!length.sanitize (c) ||
647 length < min_size ||
648 !c->check_range (this, length))
649 return_trace (false);
650
651 if (!c->check_array (featureZ, featureZ[0].static_size, featureCount))
652 return_trace (false);
653
654 const ChainSubtable *subtable = &StructAtOffset<ChainSubtable> (featureZ, featureZ[0].static_size * featureCount);
655 unsigned int count = subtableCount;
656 for (unsigned int i = 0; i < count; i++)
657 {
658 if (!subtable->sanitize (c))
659 return_trace (false);
660 subtable = &StructAfter<ChainSubtable> (*subtable);
661 }
662
663 return_trace (true);
664 }
665
666 protected:
667 HBUINT32 defaultFlags; /* The default specification for subtables. */
668 HBUINT32 length; /* Total byte count, including this header. */
669 HBUINT32 featureCount; /* Number of feature subtable entries. */
670 HBUINT32 subtableCount; /* The number of subtables in the chain. */
671
672 Feature featureZ[VAR]; /* Features. */
673/*ChainSubtable subtableX[VAR];*//* Subtables. */
674/*subtableGlyphCoverageArray*/ /* Only if major == 3. */
675
676 public:
677 DEFINE_SIZE_MIN (16);
678};
679
680
681/*
682 * The 'mort'/'morx' Tables
683 */
684
685struct morx
686{
687 static const hb_tag_t tableTag = HB_AAT_TAG_morx;
688
689 inline void apply (hb_aat_apply_context_t *c) const
690 {
691 c->set_lookup_index (0);
692 const Chain *chain = chainsZ;
693 unsigned int count = chainCount;
694 for (unsigned int i = 0; i < count; i++)
695 {
696 chain->apply (c);
697 chain = &StructAfter<Chain> (*chain);
698 }
699 }
700
701 inline bool sanitize (hb_sanitize_context_t *c) const
702 {
703 TRACE_SANITIZE (this);
704 if (!version.sanitize (c) ||
705 (version.major >> (sizeof (HBUINT32) == 4 ? 1 : 0)) != 1 ||
706 !chainCount.sanitize (c))
707 return_trace (false);
708
709 const Chain *chain = chainsZ;
710 unsigned int count = chainCount;
711 for (unsigned int i = 0; i < count; i++)
712 {
713 if (!chain->sanitize (c, version.major))
714 return_trace (false);
715 chain = &StructAfter<Chain> (*chain);
716 }
717
718 return_trace (true);
719 }
720
721 protected:
722 FixedVersion<>version; /* Version number of the glyph metamorphosis table.
723 * 1 for mort, 2 or 3 for morx. */
724 HBUINT32 chainCount; /* Number of metamorphosis chains contained in this
725 * table. */
726 Chain chainsZ[VAR]; /* Chains. */
727
728 public:
729 DEFINE_SIZE_MIN (8);
730};
731
732} /* namespace AAT */
733
734
735#endif /* HB_AAT_LAYOUT_MORX_TABLE_HH */
736