2 * Copyright © 2017 Google, Inc.
4 * This is part of HarfBuzz, a text shaping library.
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.
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
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.
24 * Google Author(s): Behdad Esfahbod
27 #ifndef HB_AAT_LAYOUT_MORX_TABLE_HH
28 #define HB_AAT_LAYOUT_MORX_TABLE_HH
30 #include "hb-open-type-private.hh"
31 #include "hb-aat-layout-common-private.hh"
33 #define HB_AAT_TAG_MORX HB_TAG('m','o','r','x')
41 struct RearrangementSubtable
43 typedef void EntryData;
45 struct driver_context_t
47 static const bool in_place = true;
49 MarkFirst = 0x8000, /* If set, make the current glyph the first
50 * glyph to be rearranged. */
51 DontAdvance = 0x4000, /* If set, don't advance to the next glyph
52 * before going to the new state. This means
53 * that the glyph index doesn't change, even
54 * if the glyph at that index has changed. */
55 MarkLast = 0x2000, /* If set, make the current glyph the last
56 * glyph to be rearranged. */
57 Reserved = 0x1FF0, /* These bits are reserved and should be set to 0. */
58 Verb = 0x000F, /* The type of rearrangement specified. */
61 inline driver_context_t (const RearrangementSubtable *table) :
65 inline bool is_actionable (StateTableDriver<EntryData> *driver,
66 const Entry<EntryData> *entry)
68 return (entry->flags & Verb) && start < end;
70 inline bool transition (StateTableDriver<EntryData> *driver,
71 const Entry<EntryData> *entry)
73 hb_buffer_t *buffer = driver->buffer;
74 unsigned int flags = entry->flags;
76 if (flags & MarkFirst)
80 end = MIN (buffer->idx + 1, buffer->len);
82 if ((flags & Verb) && start < end)
84 /* The following map has two nibbles, for start-side
85 * and end-side. Values of 0,1,2 mean move that many
86 * to the other side. Value of 3 means move 2 and
88 const unsigned char map[16] =
90 0x00, /* 0 no change */
91 0x10, /* 1 Ax => xA */
92 0x01, /* 2 xD => Dx */
93 0x11, /* 3 AxD => DxA */
94 0x20, /* 4 ABx => xAB */
95 0x30, /* 5 ABx => xBA */
96 0x02, /* 6 xCD => CDx */
97 0x03, /* 7 xCD => DCx */
98 0x12, /* 8 AxCD => CDxA */
99 0x13, /* 9 AxCD => DCxA */
100 0x21, /* 10 ABxD => DxAB */
101 0x31, /* 11 ABxD => DxBA */
102 0x22, /* 12 ABxCD => CDxAB */
103 0x32, /* 13 ABxCD => CDxBA */
104 0x23, /* 14 ABxCD => DCxAB */
105 0x33, /* 15 ABxCD => DCxBA */
108 unsigned int m = map[flags & Verb];
109 unsigned int l = MIN<unsigned int> (2, m >> 4);
110 unsigned int r = MIN<unsigned int> (2, m & 0x0F);
111 bool reverse_l = 3 == (m >> 4);
112 bool reverse_r = 3 == (m & 0x0F);
114 if (end - start >= l + r)
116 buffer->merge_clusters (start, MIN (buffer->idx + 1, buffer->len));
117 buffer->merge_clusters (start, end);
119 hb_glyph_info_t *info = buffer->info;
120 hb_glyph_info_t buf[4];
122 memcpy (buf, info + start, l * sizeof (buf[0]));
123 memcpy (buf + 2, info + end - r, r * sizeof (buf[0]));
126 memmove (info + start + r, info + start + l, (end - start - l - r) * sizeof (buf[0]));
128 memcpy (info + start, buf + 2, r * sizeof (buf[0]));
129 memcpy (info + end - l, buf, l * sizeof (buf[0]));
132 buf[0] = info[end - 1];
133 info[end - 1] = info[end - 2];
134 info[end - 2] = buf[0];
138 buf[0] = info[start];
139 info[start] = info[start + 1];
140 info[start + 1] = buf[0];
155 inline bool apply (hb_aat_apply_context_t *c) const
159 driver_context_t dc (this);
161 StateTableDriver<void> driver (machine, c->buffer, c->face);
164 return_trace (dc.ret);
167 inline bool sanitize (hb_sanitize_context_t *c) const
169 TRACE_SANITIZE (this);
170 return_trace (machine.sanitize (c));
174 StateTable<EntryData> machine;
176 DEFINE_SIZE_STATIC (16);
179 struct ContextualSubtable
183 HBUINT16 markIndex; /* Index of the substitution table for the
184 * marked glyph (use 0xFFFF for none). */
185 HBUINT16 currentIndex; /* Index of the substitution table for the
186 * current glyph (use 0xFFFF for none). */
188 DEFINE_SIZE_STATIC (4);
191 struct driver_context_t
193 static const bool in_place = true;
195 SetMark = 0x8000, /* If set, make the current glyph the marked glyph. */
196 DontAdvance = 0x4000, /* If set, don't advance to the next glyph before
197 * going to the new state. */
198 Reserved = 0x3FFF, /* These bits are reserved and should be set to 0. */
201 inline driver_context_t (const ContextualSubtable *table) :
205 subs (table+table->substitutionTables) {}
207 inline bool is_actionable (StateTableDriver<EntryData> *driver,
208 const Entry<EntryData> *entry)
210 hb_buffer_t *buffer = driver->buffer;
212 if (buffer->idx == buffer->len && !mark_set)
215 return entry->data.markIndex != 0xFFFF || entry->data.currentIndex != 0xFFFF;
217 inline bool transition (StateTableDriver<EntryData> *driver,
218 const Entry<EntryData> *entry)
220 hb_buffer_t *buffer = driver->buffer;
222 /* Looks like CoreText applies neither mark nor current substitution for
223 * end-of-text if mark was not explicitly set. */
224 if (buffer->idx == buffer->len && !mark_set)
227 if (entry->data.markIndex != 0xFFFF)
229 const Lookup<GlyphID> &lookup = subs[entry->data.markIndex];
230 hb_glyph_info_t *info = buffer->info;
231 const GlyphID *replacement = lookup.get_value (info[mark].codepoint, driver->num_glyphs);
234 buffer->unsafe_to_break (mark, MIN (buffer->idx + 1, buffer->len));
235 info[mark].codepoint = *replacement;
239 if (entry->data.currentIndex != 0xFFFF)
241 unsigned int idx = MIN (buffer->idx, buffer->len - 1);
242 const Lookup<GlyphID> &lookup = subs[entry->data.currentIndex];
243 hb_glyph_info_t *info = buffer->info;
244 const GlyphID *replacement = lookup.get_value (info[idx].codepoint, driver->num_glyphs);
247 info[idx].codepoint = *replacement;
252 if (entry->flags & SetMark)
266 const UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT32> &subs;
269 inline bool apply (hb_aat_apply_context_t *c) const
273 driver_context_t dc (this);
275 StateTableDriver<EntryData> driver (machine, c->buffer, c->face);
278 return_trace (dc.ret);
281 inline bool sanitize (hb_sanitize_context_t *c) const
283 TRACE_SANITIZE (this);
285 unsigned int num_entries = 0;
286 if (unlikely (!machine.sanitize (c, &num_entries))) return_trace (false);
288 unsigned int num_lookups = 0;
290 const Entry<EntryData> *entries = machine.get_entries ();
291 for (unsigned int i = 0; i < num_entries; i++)
293 const EntryData &data = entries[i].data;
295 if (data.markIndex != 0xFFFF)
296 num_lookups = MAX<unsigned int> (num_lookups, 1 + data.markIndex);
297 if (data.currentIndex != 0xFFFF)
298 num_lookups = MAX<unsigned int> (num_lookups, 1 + data.currentIndex);
301 return_trace (substitutionTables.sanitize (c, this, num_lookups));
305 StateTable<EntryData> machine;
306 OffsetTo<UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT32>, HBUINT32>
309 DEFINE_SIZE_STATIC (20);
312 struct LigatureSubtable
316 HBUINT16 ligActionIndex; /* Index to the first ligActionTable entry
317 * for processing this group, if indicated
320 DEFINE_SIZE_STATIC (2);
323 struct driver_context_t
325 static const bool in_place = false;
327 SetComponent = 0x8000, /* Push this glyph onto the component stack for
328 * eventual processing. */
329 DontAdvance = 0x4000, /* Leave the glyph pointer at this glyph for the
331 PerformAction = 0x2000, /* Use the ligActionIndex to process a ligature
333 Reserved = 0x1FFF, /* These bits are reserved and should be set to 0. */
335 enum LigActionFlags {
336 LigActionLast = 0x80000000, /* This is the last action in the list. This also
337 * implies storage. */
338 LigActionStore = 0x40000000, /* Store the ligature at the current cumulated index
339 * in the ligature table in place of the marked
340 * (i.e. currently-popped) glyph. */
341 LigActionOffset = 0x3FFFFFFF, /* A 30-bit value which is sign-extended to 32-bits
342 * and added to the glyph ID, resulting in an index
343 * into the component table. */
346 inline driver_context_t (const LigatureSubtable *table,
347 hb_aat_apply_context_t *c_) :
350 ligAction (table+table->ligAction),
351 component (table+table->component),
352 ligature (table+table->ligature),
355 inline bool is_actionable (StateTableDriver<EntryData> *driver,
356 const Entry<EntryData> *entry)
358 return !!(entry->flags & PerformAction);
360 inline bool transition (StateTableDriver<EntryData> *driver,
361 const Entry<EntryData> *entry)
363 hb_buffer_t *buffer = driver->buffer;
364 unsigned int flags = entry->flags;
366 if (flags & SetComponent)
368 if (unlikely (match_length >= ARRAY_LENGTH (match_positions)))
371 /* Never mark same index twice, in case DontAdvance was used... */
372 if (match_length && match_positions[match_length - 1] == buffer->out_len)
375 match_positions[match_length++] = buffer->out_len;
378 if (flags & PerformAction)
380 unsigned int end = buffer->out_len;
381 unsigned int action_idx = entry->data.ligActionIndex;
383 unsigned int ligature_idx = 0;
386 if (unlikely (!match_length))
389 buffer->move_to (match_positions[--match_length]);
391 const HBUINT32 &actionData = ligAction[action_idx];
392 if (unlikely (!actionData.sanitize (&c->sanitizer))) return false;
395 uint32_t uoffset = action & LigActionOffset;
396 if (uoffset & 0x20000000)
397 uoffset += 0xC0000000;
398 int32_t offset = (int32_t) uoffset;
399 unsigned int component_idx = buffer->cur().codepoint + offset;
401 const HBUINT16 &componentData = component[component_idx];
402 if (unlikely (!componentData.sanitize (&c->sanitizer))) return false;
403 ligature_idx += componentData;
405 if (action & (LigActionStore | LigActionLast))
407 const GlyphID &ligatureData = ligature[ligature_idx];
408 if (unlikely (!ligatureData.sanitize (&c->sanitizer))) return false;
409 hb_codepoint_t lig = ligatureData;
411 match_positions[match_length++] = buffer->out_len;
412 buffer->replace_glyph (lig);
414 //ligature_idx = 0; // XXX Yes or no?
418 buffer->skip_glyph ();
421 /* TODO merge_clusters / unsafe_to_break */
425 while (!(action & LigActionLast));
426 buffer->move_to (end);
435 hb_aat_apply_context_t *c;
436 const UnsizedArrayOf<HBUINT32> &ligAction;
437 const UnsizedArrayOf<HBUINT16> &component;
438 const UnsizedArrayOf<GlyphID> &ligature;
439 unsigned int match_length;
440 unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
443 inline bool apply (hb_aat_apply_context_t *c) const
447 driver_context_t dc (this, c);
449 StateTableDriver<EntryData> driver (machine, c->buffer, c->face);
452 return_trace (dc.ret);
455 inline bool sanitize (hb_sanitize_context_t *c) const
457 TRACE_SANITIZE (this);
458 /* The rest of array sanitizations are done at run-time. */
459 return_trace (c->check_struct (this) && machine.sanitize (c) &&
460 ligAction && component && ligature);
464 StateTable<EntryData> machine;
465 OffsetTo<UnsizedArrayOf<HBUINT32>, HBUINT32>
466 ligAction; /* Offset to the ligature action table. */
467 OffsetTo<UnsizedArrayOf<HBUINT16>, HBUINT32>
468 component; /* Offset to the component table. */
469 OffsetTo<UnsizedArrayOf<GlyphID>, HBUINT32>
470 ligature; /* Offset to the actual ligature lists. */
472 DEFINE_SIZE_STATIC (28);
475 struct NoncontextualSubtable
477 inline bool apply (hb_aat_apply_context_t *c) const
482 unsigned int num_glyphs = c->face->get_num_glyphs ();
484 hb_glyph_info_t *info = c->buffer->info;
485 unsigned int count = c->buffer->len;
486 for (unsigned int i = 0; i < count; i++)
488 const GlyphID *replacement = substitute.get_value (info[i].codepoint, num_glyphs);
491 info[i].codepoint = *replacement;
499 inline bool sanitize (hb_sanitize_context_t *c) const
501 TRACE_SANITIZE (this);
502 return_trace (substitute.sanitize (c));
506 Lookup<GlyphID> substitute;
511 struct InsertionSubtable
513 inline bool apply (hb_aat_apply_context_t *c) const
517 return_trace (false);
520 inline bool sanitize (hb_sanitize_context_t *c) const
522 TRACE_SANITIZE (this);
531 inline bool sanitize (hb_sanitize_context_t *c) const
533 TRACE_SANITIZE (this);
534 return_trace (c->check_struct (this));
538 HBUINT16 featureType; /* The type of feature. */
539 HBUINT16 featureSetting; /* The feature's setting (aka selector). */
540 HBUINT32 enableFlags; /* Flags for the settings that this feature
541 * and setting enables. */
542 HBUINT32 disableFlags; /* Complement of flags for the settings that this
543 * feature and setting disable. */
546 DEFINE_SIZE_STATIC (12);
554 inline unsigned int get_size (void) const { return length; }
555 inline unsigned int get_type (void) const { return coverage & 0xFF; }
565 inline void apply (hb_aat_apply_context_t *c) const
570 template <typename context_t>
571 inline typename context_t::return_t dispatch (context_t *c) const
573 unsigned int subtable_type = get_type ();
574 TRACE_DISPATCH (this, subtable_type);
575 switch (subtable_type) {
576 case Rearrangement: return_trace (c->dispatch (u.rearrangement));
577 case Contextual: return_trace (c->dispatch (u.contextual));
578 case Ligature: return_trace (c->dispatch (u.ligature));
579 case Noncontextual: return_trace (c->dispatch (u.noncontextual));
580 case Insertion: return_trace (c->dispatch (u.insertion));
581 default: return_trace (c->default_return_value ());
585 inline bool sanitize (hb_sanitize_context_t *c) const
587 TRACE_SANITIZE (this);
588 if (!length.sanitize (c) ||
590 !c->check_range (this, length))
591 return_trace (false);
593 return_trace (dispatch (c));
597 HBUINT32 length; /* Total subtable length, including this header. */
598 HBUINT32 coverage; /* Coverage flags and subtable type. */
599 HBUINT32 subFeatureFlags;/* The 32-bit mask identifying which subtable this is. */
601 RearrangementSubtable rearrangement;
602 ContextualSubtable contextual;
603 LigatureSubtable ligature;
604 NoncontextualSubtable noncontextual;
605 InsertionSubtable insertion;
608 DEFINE_SIZE_MIN (12);
613 inline void apply (hb_aat_apply_context_t *c) const
615 const ChainSubtable *subtable = &StructAtOffset<ChainSubtable> (featureZ, featureZ[0].static_size * featureCount);
616 unsigned int count = subtableCount;
617 for (unsigned int i = 0; i < count; i++)
619 if (!c->buffer->message (c->font, "start chain subtable %d", c->lookup_index))
621 c->set_lookup_index (c->lookup_index + 1);
626 subtable = &StructAfter<ChainSubtable> (*subtable);
628 (void) c->buffer->message (c->font, "end chain subtable %d", c->lookup_index);
630 c->set_lookup_index (c->lookup_index + 1);
634 inline unsigned int get_size (void) const { return length; }
636 inline bool sanitize (hb_sanitize_context_t *c, unsigned int major) const
638 TRACE_SANITIZE (this);
639 if (!length.sanitize (c) ||
641 !c->check_range (this, length))
642 return_trace (false);
644 if (!c->check_array (featureZ, featureZ[0].static_size, featureCount))
645 return_trace (false);
647 const ChainSubtable *subtable = &StructAtOffset<ChainSubtable> (featureZ, featureZ[0].static_size * featureCount);
648 unsigned int count = subtableCount;
649 for (unsigned int i = 0; i < count; i++)
651 if (!subtable->sanitize (c))
652 return_trace (false);
653 subtable = &StructAfter<ChainSubtable> (*subtable);
660 HBUINT32 defaultFlags; /* The default specification for subtables. */
661 HBUINT32 length; /* Total byte count, including this header. */
662 HBUINT32 featureCount; /* Number of feature subtable entries. */
663 HBUINT32 subtableCount; /* The number of subtables in the chain. */
665 Feature featureZ[VAR]; /* Features. */
666 ChainSubtable subtableX[VAR]; /* Subtables. */
667 // subtableGlyphCoverageArray if major == 3
670 DEFINE_SIZE_MIN (16);
675 * The 'mort'/'morx' Tables
680 static const hb_tag_t tableTag = HB_AAT_TAG_MORX;
682 inline void apply (hb_aat_apply_context_t *c) const
684 c->set_lookup_index (0);
685 const Chain *chain = chains;
686 unsigned int count = chainCount;
687 for (unsigned int i = 0; i < count; i++)
690 chain = &StructAfter<Chain> (*chain);
694 inline bool sanitize (hb_sanitize_context_t *c) const
696 TRACE_SANITIZE (this);
697 if (!version.sanitize (c) ||
698 (version.major >> (sizeof (HBUINT32) == 4 ? 1 : 0)) != 1 ||
699 !chainCount.sanitize (c))
700 return_trace (false);
702 const Chain *chain = chains;
703 unsigned int count = chainCount;
704 for (unsigned int i = 0; i < count; i++)
706 if (!chain->sanitize (c, version.major))
707 return_trace (false);
708 chain = &StructAfter<Chain> (*chain);
715 FixedVersion<>version; /* Version number of the glyph metamorphosis table.
716 * 1 for mort, 2 or 3 for morx. */
717 HBUINT32 chainCount; /* Number of metamorphosis chains contained in this
719 Chain chains[VAR]; /* Chains. */
725 } /* namespace AAT */
728 #endif /* HB_AAT_LAYOUT_MORX_TABLE_HH */