• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
45 namespace AAT {
46 
47 using namespace OT;
48 
49 template <typename Types>
50 struct 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 
driver_context_tAAT::RearrangementSubtable::driver_context_t73     driver_context_t (const RearrangementSubtable *table HB_UNUSED) :
74 	ret (false),
75 	start (0), end (0) {}
76 
is_actionableAAT::RearrangementSubtable::driver_context_t77     bool is_actionable (StateTableDriver<Types, EntryData> *driver HB_UNUSED,
78 			const Entry<EntryData> &entry)
79     {
80       return (entry.flags & Verb) && start < end;
81     }
transitionAAT::RearrangementSubtable::driver_context_t82     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 
applyAAT::RearrangementSubtable165   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 
sanitizeAAT::RearrangementSubtable177   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 
189 template <typename Types>
190 struct 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 
driver_context_tAAT::ContextualSubtable::driver_context_t215     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 
is_actionableAAT::ContextualSubtable::driver_context_t226     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     }
transitionAAT::ContextualSubtable::driver_context_t236     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 
applyAAT::ContextualSubtable321   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 
sanitizeAAT::ContextualSubtable333   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 
369 template <bool extended>
370 struct LigatureEntry;
371 
372 template <>
373 struct 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 
performActionAAT::LigatureEntry395   static bool performAction (const Entry<EntryData> &entry)
396   { return entry.flags & PerformAction; }
397 
ligActionIndexAAT::LigatureEntry398   static unsigned int ligActionIndex (const Entry<EntryData> &entry)
399   { return entry.data.ligActionIndex; }
400 };
401 template <>
402 struct 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 
performActionAAT::LigatureEntry417   static bool performAction (const Entry<EntryData> &entry)
418   { return entry.flags & Offset; }
419 
ligActionIndexAAT::LigatureEntry420   static unsigned int ligActionIndex (const Entry<EntryData> &entry)
421   { return entry.flags & Offset; }
422 };
423 
424 
425 template <typename Types>
426 struct 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 
driver_context_tAAT::LigatureSubtable::driver_context_t452     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 
is_actionableAAT::LigatureSubtable::driver_context_t462     bool is_actionable (StateTableDriver<Types, EntryData> *driver HB_UNUSED,
463 			const Entry<EntryData> &entry)
464     {
465       return LigatureEntryT::performAction (entry);
466     }
transitionAAT::LigatureSubtable::driver_context_t467     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 
applyAAT::LigatureSubtable573   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 
sanitizeAAT::LigatureSubtable585   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 
606 template <typename Types>
607 struct NoncontextualSubtable
608 {
applyAAT::NoncontextualSubtable609   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 
sanitizeAAT::NoncontextualSubtable656   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 
668 template <typename Types>
669 struct 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 
driver_context_tAAT::InsertionSubtable::driver_context_t740     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 
is_actionableAAT::InsertionSubtable::driver_context_t747     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     }
transitionAAT::InsertionSubtable::driver_context_t753     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 
applyAAT::InsertionSubtable835   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 
sanitizeAAT::InsertionSubtable847   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 
866 struct Feature
867 {
sanitizeAAT::Feature868   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 
886 template <typename Types>
887 struct ChainSubtable
888 {
889   typedef typename Types::HBUINT HBUINT;
890 
891   template <typename T>
892   friend struct Chain;
893 
get_sizeAAT::ChainSubtable894   unsigned int get_size () const     { return length; }
get_typeAAT::ChainSubtable895   unsigned int get_type () const     { return coverage & 0xFF; }
get_coverageAAT::ChainSubtable896   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>
dispatchAAT::ChainSubtable923   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 
applyAAT::ChainSubtable937   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 
sanitizeAAT::ChainSubtable944   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 
971 template <typename Types>
972 struct Chain
973 {
974   typedef typename Types::HBUINT HBUINT;
975 
compile_flagsAAT::Chain976   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 
applyAAT::Chain1016   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 
get_sizeAAT::Chain1087   unsigned int get_size () const { return length; }
1088 
sanitizeAAT::Chain1089   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 
1131 template <typename Types, hb_tag_t TAG>
1132 struct mortmorx
1133 {
1134   static constexpr hb_tag_t tableTag = TAG;
1135 
has_dataAAT::mortmorx1136   bool has_data () const { return version != 0; }
1137 
compile_flagsAAT::mortmorx1138   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 
applyAAT::mortmorx1154   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 
sanitizeAAT::mortmorx1173   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 
1203 struct morx : mortmorx<ExtendedTypes, HB_AAT_TAG_morx> {};
1204 struct mort : mortmorx<ObsoleteTypes, HB_AAT_TAG_mort> {};
1205 
1206 
1207 } /* namespace AAT */
1208 
1209 
1210 #endif /* HB_AAT_LAYOUT_MORX_TABLE_HH */
1211