hb-aat-layout-morx-table.hh 19.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
/*
 * Copyright © 2017  Google, Inc.
 *
 *  This is part of HarfBuzz, a text shaping library.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 *
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Google Author(s): Behdad Esfahbod
 */

#ifndef HB_AAT_LAYOUT_MORX_TABLE_HH
#define HB_AAT_LAYOUT_MORX_TABLE_HH

30 31
#include "hb-open-type-private.hh"
#include "hb-aat-layout-common-private.hh"
32 33 34 35 36 37 38 39 40 41 42

#define HB_AAT_TAG_MORX HB_TAG('m','o','r','x')


namespace AAT {

using namespace OT;


struct RearrangementSubtable
{
B
Minor  
Behdad Esfahbod 已提交
43 44
  typedef void EntryData;

45
  struct driver_context_t
46
  {
47
    static const bool in_place = true;
48
    enum Flags {
B
Behdad Esfahbod 已提交
49 50
      MarkFirst		= 0x8000,	/* If set, make the current glyph the first
					 * glyph to be rearranged. */
51
      DontAdvance	= 0x4000,	/* If set, don't advance to the next glyph
B
Behdad Esfahbod 已提交
52 53 54 55 56 57 58
					 * before going to the new state. This means
					 * that the glyph index doesn't change, even
					 * if the glyph at that index has changed. */
      MarkLast		= 0x2000,	/* If set, make the current glyph the last
					 * glyph to be rearranged. */
      Reserved		= 0x1FF0,	/* These bits are reserved and should be set to 0. */
      Verb		= 0x000F,	/* The type of rearrangement specified. */
59 60 61 62
    };

    inline driver_context_t (const RearrangementSubtable *table) :
	ret (false),
B
Behdad Esfahbod 已提交
63
	start (0), end (0) {}
64

B
Minor  
Behdad Esfahbod 已提交
65 66
    inline bool is_actionable (StateTableDriver<EntryData> *driver,
			       const Entry<EntryData> *entry)
B
Behdad Esfahbod 已提交
67 68 69
    {
      return (entry->flags & Verb) && start < end;
    }
B
Minor  
Behdad Esfahbod 已提交
70 71
    inline bool transition (StateTableDriver<EntryData> *driver,
			    const Entry<EntryData> *entry)
72
    {
73
      hb_buffer_t *buffer = driver->buffer;
74 75 76
      unsigned int flags = entry->flags;

      if (flags & MarkFirst)
77
	start = buffer->idx;
78

79
      if (flags & MarkLast)
80
	end = MIN (buffer->idx + 1, buffer->len);
81 82 83 84 85 86 87

      if ((flags & Verb) && start < end)
      {
	/* The following map has two nibbles, for start-side
	 * and end-side. Values of 0,1,2 mean move that many
	 * to the other side. Value of 3 means move 2 and
	 * flip them. */
B
Behdad Esfahbod 已提交
88
	const unsigned char map[16] =
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
	{
	  0x00,	/* 0	no change */
	  0x10,	/* 1	Ax => xA */
	  0x01,	/* 2	xD => Dx */
	  0x11,	/* 3	AxD => DxA */
	  0x20,	/* 4	ABx => xAB */
	  0x30,	/* 5	ABx => xBA */
	  0x02,	/* 6	xCD => CDx */
	  0x03,	/* 7	xCD => DCx */
	  0x12,	/* 8	AxCD => CDxA */
	  0x13,	/* 9	AxCD => DCxA */
	  0x21,	/* 10	ABxD => DxAB */
	  0x31,	/* 11	ABxD => DxBA */
	  0x22,	/* 12	ABxCD => CDxAB */
	  0x32,	/* 13	ABxCD => CDxBA */
	  0x23,	/* 14	ABxCD => DCxAB */
	  0x33,	/* 15	ABxCD => DCxBA */
	};

	unsigned int m = map[flags & Verb];
	unsigned int l = MIN<unsigned int> (2, m >> 4);
	unsigned int r = MIN<unsigned int> (2, m & 0x0F);
	bool reverse_l = 3 == (m >> 4);
	bool reverse_r = 3 == (m & 0x0F);

	if (end - start >= l + r)
	{
B
Behdad Esfahbod 已提交
116
	  buffer->merge_clusters (start, MIN (buffer->idx + 1, buffer->len));
117
	  buffer->merge_clusters (start, end);
118

119
	  hb_glyph_info_t *info = buffer->info;
120
	  hb_glyph_info_t buf[4];
121

122 123 124 125
	  memcpy (buf, info + start, l * sizeof (buf[0]));
	  memcpy (buf + 2, info + end - r, r * sizeof (buf[0]));

	  if (l != r)
126
	    memmove (info + start + r, info + start + l, (end - start - l - r) * sizeof (buf[0]));
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143

	  memcpy (info + start, buf + 2, r * sizeof (buf[0]));
	  memcpy (info + end - l, buf, l * sizeof (buf[0]));
	  if (reverse_l)
	  {
	    buf[0] = info[end - 1];
	    info[end - 1] = info[end - 2];
	    info[end - 2] = buf[0];
	  }
	  if (reverse_r)
	  {
	    buf[0] = info[start];
	    info[start] = info[start + 1];
	    info[start + 1] = buf[0];
	  }
	}
      }
144 145

      return true;
146 147 148 149 150
    }

    public:
    bool ret;
    private:
B
Behdad Esfahbod 已提交
151 152
    unsigned int start;
    unsigned int end;
153
  };
154

155
  inline bool apply (hb_aat_apply_context_t *c) const
156 157
  {
    TRACE_APPLY (this);
158

159 160 161 162
    driver_context_t dc (this);

    StateTableDriver<void> driver (machine, c->buffer, c->face);
    driver.drive (&dc);
163

164
    return_trace (dc.ret);
165 166
  }

167 168 169
  inline bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
170
    return_trace (machine.sanitize (c));
171
  }
172 173

  protected:
B
Behdad Esfahbod 已提交
174
  StateTable<void>	machine;
175
  public:
B
Behdad Esfahbod 已提交
176
  DEFINE_SIZE_STATIC (16);
177 178 179 180
};

struct ContextualSubtable
{
181 182 183 184 185 186
  struct EntryData
  {
    HBUINT16	markIndex;	/* Index of the substitution table for the
				 * marked glyph (use 0xFFFF for none). */
    HBUINT16	currentIndex;	/* Index of the substitution table for the
				 * current glyph (use 0xFFFF for none). */
B
Behdad Esfahbod 已提交
187
    public:
188
    DEFINE_SIZE_STATIC (4);
189 190
  };

191
  struct driver_context_t
192
  {
193
    static const bool in_place = true;
194 195 196 197 198 199 200 201 202
    enum Flags {
      SetMark		= 0x8000,	/* If set, make the current glyph the marked glyph. */
      DontAdvance	= 0x4000,	/* If set, don't advance to the next glyph before
					 * going to the new state. */
      Reserved		= 0x3FFF,	/* These bits are reserved and should be set to 0. */
    };

    inline driver_context_t (const ContextualSubtable *table) :
	ret (false),
203
	mark_set (false),
204 205 206
	mark (0),
	subs (table+table->substitutionTables) {}

B
Behdad Esfahbod 已提交
207 208 209 210 211 212 213 214 215 216
    inline bool is_actionable (StateTableDriver<EntryData> *driver,
			       const Entry<EntryData> *entry)
    {
      hb_buffer_t *buffer = driver->buffer;

      if (buffer->idx == buffer->len && !mark_set)
        return false;

      return entry->data.markIndex != 0xFFFF || entry->data.currentIndex != 0xFFFF;
    }
217
    inline bool transition (StateTableDriver<EntryData> *driver,
218
			    const Entry<EntryData> *entry)
219
    {
220
      hb_buffer_t *buffer = driver->buffer;
221

222 223 224 225 226
      /* Looks like CoreText applies neither mark nor current substitution for
       * end-of-text if mark was not explicitly set. */
      if (buffer->idx == buffer->len && !mark_set)
        return true;

B
Behdad Esfahbod 已提交
227
      if (entry->data.markIndex != 0xFFFF)
228
      {
229
	const Lookup<GlyphID> &lookup = subs[entry->data.markIndex];
230 231
	hb_glyph_info_t *info = buffer->info;
	const GlyphID *replacement = lookup.get_value (info[mark].codepoint, driver->num_glyphs);
232 233
	if (replacement)
	{
B
Behdad Esfahbod 已提交
234
	  buffer->unsafe_to_break (mark, MIN (buffer->idx + 1, buffer->len));
235 236 237 238
	  info[mark].codepoint = *replacement;
	  ret = true;
	}
      }
239
      if (entry->data.currentIndex != 0xFFFF)
240
      {
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);
245 246
	if (replacement)
	{
247
	  info[idx].codepoint = *replacement;
248 249 250
	  ret = true;
	}
      }
251

252 253
      if (entry->flags & SetMark)
      {
254
	mark_set = true;
255 256 257
	mark = buffer->idx;
      }

258
      return true;
259
    }
260

261 262 263
    public:
    bool ret;
    private:
264
    bool mark_set;
265 266 267
    unsigned int mark;
    const UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT32> &subs;
  };
268

269
  inline bool apply (hb_aat_apply_context_t *c) const
270 271
  {
    TRACE_APPLY (this);
272

273 274 275 276 277 278
    driver_context_t dc (this);

    StateTableDriver<EntryData> driver (machine, c->buffer, c->face);
    driver.drive (&dc);

    return_trace (dc.ret);
279 280
  }

281 282 283
  inline bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
284

B
Behdad Esfahbod 已提交
285
    unsigned int num_entries = 0;
B
Behdad Esfahbod 已提交
286
    if (unlikely (!machine.sanitize (c, &num_entries))) return_trace (false);
287 288 289 290 291 292 293 294

    unsigned int num_lookups = 0;

    const Entry<EntryData> *entries = machine.get_entries ();
    for (unsigned int i = 0; i < num_entries; i++)
    {
      const EntryData &data = entries[i].data;

295 296 297 298
      if (data.markIndex != 0xFFFF)
	num_lookups = MAX<unsigned int> (num_lookups, 1 + data.markIndex);
      if (data.currentIndex != 0xFFFF)
	num_lookups = MAX<unsigned int> (num_lookups, 1 + data.currentIndex);
299 300 301
    }

    return_trace (substitutionTables.sanitize (c, this, num_lookups));
302
  }
303 304

  protected:
B
Behdad Esfahbod 已提交
305 306 307
  StateTable<EntryData>	machine;
  OffsetTo<UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT32>, HBUINT32>
			substitutionTables;
308
  public:
B
Behdad Esfahbod 已提交
309
  DEFINE_SIZE_STATIC (20);
310 311 312 313
};

struct LigatureSubtable
{
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
  struct EntryData
  {
    HBUINT16	ligActionIndex;	/* Index to the first ligActionTable entry
				 * for processing this group, if indicated
				 * by the flags. */
    public:
    DEFINE_SIZE_STATIC (2);
  };

  struct driver_context_t
  {
    static const bool in_place = false;
    enum Flags {
      SetComponent	= 0x8000,	/* Push this glyph onto the component stack for
					 * eventual processing. */
      DontAdvance	= 0x4000,	/* Leave the glyph pointer at this glyph for the
					   next iteration. */
      PerformAction	= 0x2000,	/* Use the ligActionIndex to process a ligature
					 * group. */
      Reserved		= 0x1FFF,	/* These bits are reserved and should be set to 0. */
    };
335 336 337 338 339 340 341 342 343 344
    enum LigActionFlags {
      LigActionLast	= 0x80000000,	/* This is the last action in the list. This also
					 * implies storage. */
      LigActionStore	= 0x40000000,	/* Store the ligature at the current cumulated index
					 * in the ligature table in place of the marked
					 * (i.e. currently-popped) glyph. */
      LigActionOffset	= 0x3FFFFFFF,	/* A 30-bit value which is sign-extended to 32-bits
					 * and added to the glyph ID, resulting in an index
					 * into the component table. */
    };
345

346 347 348 349 350 351 352 353
    inline driver_context_t (const LigatureSubtable *table,
			     hb_aat_apply_context_t *c_) :
	ret (false),
	c (c_),
	ligAction (table+table->ligAction),
	component (table+table->component),
	ligature (table+table->ligature),
	match_length (0) {}
354

B
Behdad Esfahbod 已提交
355 356 357 358 359
    inline bool is_actionable (StateTableDriver<EntryData> *driver,
			       const Entry<EntryData> *entry)
    {
      return !!(entry->flags & PerformAction);
    }
360
    inline bool transition (StateTableDriver<EntryData> *driver,
361 362 363
			    const Entry<EntryData> *entry)
    {
      hb_buffer_t *buffer = driver->buffer;
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
      unsigned int flags = entry->flags;

      if (flags & SetComponent)
      {
        if (unlikely (match_length >= ARRAY_LENGTH (match_positions)))
	  return false;

	/* Never mark same index twice, in case DontAdvance was used... */
	if (match_length && match_positions[match_length - 1] == buffer->out_len)
	  match_length--;

	match_positions[match_length++] = buffer->out_len;
      }

      if (flags & PerformAction)
      {
	unsigned int end = buffer->out_len;
	unsigned int action_idx = entry->data.ligActionIndex;
	unsigned int action;
	unsigned int ligature_idx = 0;
        do
	{
	  if (unlikely (!match_length))
	    return false;

B
Behdad Esfahbod 已提交
389
	  buffer->move_to (match_positions[--match_length]);
390

391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
	  const HBUINT32 &actionData = ligAction[action_idx];
	  if (unlikely (!actionData.sanitize (&c->sanitizer))) return false;
	  action = actionData;

	  uint32_t uoffset = action & LigActionOffset;
	  if (uoffset & 0x20000000)
	    uoffset += 0xC0000000;
	  int32_t offset = (int32_t) uoffset;
	  unsigned int component_idx = buffer->cur().codepoint + offset;

	  const HBUINT16 &componentData = component[component_idx];
	  if (unlikely (!componentData.sanitize (&c->sanitizer))) return false;
	  ligature_idx += componentData;

	  if (action & (LigActionStore | LigActionLast))
	  {
	    const GlyphID &ligatureData = ligature[ligature_idx];
	    if (unlikely (!ligatureData.sanitize (&c->sanitizer))) return false;
	    hb_codepoint_t lig = ligatureData;

B
Behdad Esfahbod 已提交
411
	    match_positions[match_length++] = buffer->out_len;
412 413 414 415 416 417 418 419 420
	    buffer->replace_glyph (lig);

	    //ligature_idx = 0; // XXX Yes or no?
	  }
	  else
	  {
	    buffer->skip_glyph ();
	    end--;
	  }
B
Behdad Esfahbod 已提交
421
	  /* TODO merge_clusters / unsafe_to_break */
422 423 424 425 426 427 428 429

	  action_idx++;
	}
	while (!(action & LigActionLast));
	buffer->move_to (end);
      }

      return true;
430 431 432 433 434
    }

    public:
    bool ret;
    private:
435 436 437 438 439 440
    hb_aat_apply_context_t *c;
    const UnsizedArrayOf<HBUINT32> &ligAction;
    const UnsizedArrayOf<HBUINT16> &component;
    const UnsizedArrayOf<GlyphID> &ligature;
    unsigned int match_length;
    unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
441 442
  };

443
  inline bool apply (hb_aat_apply_context_t *c) const
444 445
  {
    TRACE_APPLY (this);
446

447
    driver_context_t dc (this, c);
448 449 450 451 452

    StateTableDriver<EntryData> driver (machine, c->buffer, c->face);
    driver.drive (&dc);

    return_trace (dc.ret);
453 454
  }

455 456 457
  inline bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
458
    /* The rest of array sanitizations are done at run-time. */
B
Behdad Esfahbod 已提交
459 460
    return_trace (c->check_struct (this) && machine.sanitize (c) &&
		  ligAction && component && ligature);
461
  }
462 463 464 465 466 467 468 469 470 471 472

  protected:
  StateTable<EntryData>	machine;
  OffsetTo<UnsizedArrayOf<HBUINT32>, HBUINT32>
		ligAction;	/* Offset to the ligature action table. */
  OffsetTo<UnsizedArrayOf<HBUINT16>, HBUINT32>
		component;	/* Offset to the component table. */
  OffsetTo<UnsizedArrayOf<GlyphID>, HBUINT32>
		ligature;	/* Offset to the actual ligature lists. */
  public:
  DEFINE_SIZE_STATIC (28);
473 474 475 476
};

struct NoncontextualSubtable
{
477
  inline bool apply (hb_aat_apply_context_t *c) const
478 479
  {
    TRACE_APPLY (this);
480

481
    bool ret = false;
482 483 484 485
    unsigned int num_glyphs = c->face->get_num_glyphs ();

    hb_glyph_info_t *info = c->buffer->info;
    unsigned int count = c->buffer->len;
486 487 488 489 490 491 492 493 494
    for (unsigned int i = 0; i < count; i++)
    {
      const GlyphID *replacement = substitute.get_value (info[i].codepoint, num_glyphs);
      if (replacement)
      {
	info[i].codepoint = *replacement;
	ret = true;
      }
    }
495

496 497 498
    return_trace (ret);
  }

499 500 501
  inline bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
B
Behdad Esfahbod 已提交
502
    return_trace (substitute.sanitize (c));
503
  }
B
Behdad Esfahbod 已提交
504 505 506 507 508

  protected:
  Lookup<GlyphID>	substitute;
  public:
  DEFINE_SIZE_MIN (2);
509 510 511 512
};

struct InsertionSubtable
{
513
  inline bool apply (hb_aat_apply_context_t *c) const
514 515 516 517 518 519
  {
    TRACE_APPLY (this);
    /* TODO */
    return_trace (false);
  }

520 521 522 523
  inline bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    /* TODO */
524
    return_trace (true);
525 526 527 528 529 530 531 532 533 534 535 536 537
  }
};


struct Feature
{
  inline bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this));
  }

  public:
B
Behdad Esfahbod 已提交
538 539 540
  HBUINT16	featureType;	/* The type of feature. */
  HBUINT16	featureSetting;	/* The feature's setting (aka selector). */
  HBUINT32	enableFlags;	/* Flags for the settings that this feature
541
				 * and setting enables. */
B
Behdad Esfahbod 已提交
542
  HBUINT32	disableFlags;	/* Complement of flags for the settings that this
543 544 545 546 547 548 549 550 551
				 * feature and setting disable. */

  public:
  DEFINE_SIZE_STATIC (12);
};


struct ChainSubtable
{
B
Behdad Esfahbod 已提交
552
  friend struct Chain;
553 554 555 556 557 558 559 560 561 562 563 564

  inline unsigned int get_size (void) const { return length; }
  inline unsigned int get_type (void) const { return coverage & 0xFF; }

  enum Type {
    Rearrangement	= 0,
    Contextual		= 1,
    Ligature		= 2,
    Noncontextual	= 4,
    Insertion		= 5
  };

565
  inline void apply (hb_aat_apply_context_t *c) const
566
  {
567
    dispatch (c);
568 569
  }

570
  template <typename context_t>
571
  inline typename context_t::return_t dispatch (context_t *c) const
572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592
  {
    unsigned int subtable_type = get_type ();
    TRACE_DISPATCH (this, subtable_type);
    switch (subtable_type) {
    case Rearrangement:		return_trace (c->dispatch (u.rearrangement));
    case Contextual:		return_trace (c->dispatch (u.contextual));
    case Ligature:		return_trace (c->dispatch (u.ligature));
    case Noncontextual:		return_trace (c->dispatch (u.noncontextual));
    case Insertion:		return_trace (c->dispatch (u.insertion));
    default:			return_trace (c->default_return_value ());
    }
  }

  inline bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    if (!length.sanitize (c) ||
	length < min_size ||
	!c->check_range (this, length))
      return_trace (false);

593
    return_trace (dispatch (c));
594 595 596
  }

  protected:
B
Behdad Esfahbod 已提交
597 598 599
  HBUINT32	length;		/* Total subtable length, including this header. */
  HBUINT32	coverage;	/* Coverage flags and subtable type. */
  HBUINT32	subFeatureFlags;/* The 32-bit mask identifying which subtable this is. */
600
  union {
B
Behdad Esfahbod 已提交
601 602 603 604 605
  RearrangementSubtable		rearrangement;
  ContextualSubtable		contextual;
  LigatureSubtable		ligature;
  NoncontextualSubtable		noncontextual;
  InsertionSubtable		insertion;
606 607
  } u;
  public:
B
Behdad Esfahbod 已提交
608
  DEFINE_SIZE_MIN (12);
609 610 611 612
};

struct Chain
{
613
  inline void apply (hb_aat_apply_context_t *c) const
614
  {
B
Behdad Esfahbod 已提交
615
    const ChainSubtable *subtable = &StructAtOffset<ChainSubtable> (featureZ, featureZ[0].static_size * featureCount);
616 617 618
    unsigned int count = subtableCount;
    for (unsigned int i = 0; i < count; i++)
    {
B
Behdad Esfahbod 已提交
619 620 621 622 623 624
      if (!c->buffer->message (c->font, "start chain subtable %d", c->lookup_index))
      {
	c->set_lookup_index (c->lookup_index + 1);
	continue;
      }

625
      subtable->apply (c);
B
Behdad Esfahbod 已提交
626
      subtable = &StructAfter<ChainSubtable> (*subtable);
B
Behdad Esfahbod 已提交
627

B
Behdad Esfahbod 已提交
628 629
      (void) c->buffer->message (c->font, "end chain subtable %d", c->lookup_index);

B
Behdad Esfahbod 已提交
630
      c->set_lookup_index (c->lookup_index + 1);
631 632
    }
  }
633 634 635 636 637 638 639 640 641 642 643 644 645 646

  inline unsigned int get_size (void) const { return length; }

  inline bool sanitize (hb_sanitize_context_t *c, unsigned int major) const
  {
    TRACE_SANITIZE (this);
    if (!length.sanitize (c) ||
	length < min_size ||
	!c->check_range (this, length))
      return_trace (false);

    if (!c->check_array (featureZ, featureZ[0].static_size, featureCount))
      return_trace (false);

B
Behdad Esfahbod 已提交
647
    const ChainSubtable *subtable = &StructAtOffset<ChainSubtable> (featureZ, featureZ[0].static_size * featureCount);
648 649 650 651 652
    unsigned int count = subtableCount;
    for (unsigned int i = 0; i < count; i++)
    {
      if (!subtable->sanitize (c))
	return_trace (false);
B
Behdad Esfahbod 已提交
653
      subtable = &StructAfter<ChainSubtable> (*subtable);
654 655 656 657 658 659
    }

    return_trace (true);
  }

  protected:
B
Behdad Esfahbod 已提交
660 661
  HBUINT32	defaultFlags;	/* The default specification for subtables. */
  HBUINT32	length;		/* Total byte count, including this header. */
B
Behdad Esfahbod 已提交
662 663
  HBUINT32	featureCount;	/* Number of feature subtable entries. */
  HBUINT32	subtableCount;	/* The number of subtables in the chain. */
664

B
Behdad Esfahbod 已提交
665 666
  Feature	featureZ[VAR];	/* Features. */
  ChainSubtable	subtableX[VAR];	/* Subtables. */
667 668 669
  // subtableGlyphCoverageArray if major == 3

  public:
B
Behdad Esfahbod 已提交
670
  DEFINE_SIZE_MIN (16);
671 672 673 674 675 676 677
};


/*
 * The 'mort'/'morx' Tables
 */

B
Behdad Esfahbod 已提交
678
struct morx
679
{
B
Behdad Esfahbod 已提交
680
  static const hb_tag_t tableTag = HB_AAT_TAG_MORX;
681

682
  inline void apply (hb_aat_apply_context_t *c) const
683
  {
B
Behdad Esfahbod 已提交
684
    c->set_lookup_index (0);
B
Behdad Esfahbod 已提交
685
    const Chain *chain = chains;
686 687 688
    unsigned int count = chainCount;
    for (unsigned int i = 0; i < count; i++)
    {
689
      chain->apply (c);
B
Behdad Esfahbod 已提交
690
      chain = &StructAfter<Chain> (*chain);
691 692 693
    }
  }

694 695 696 697
  inline bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    if (!version.sanitize (c) ||
B
Behdad Esfahbod 已提交
698
	(version.major >> (sizeof (HBUINT32) == 4 ? 1 : 0)) != 1 ||
699 700 701
	!chainCount.sanitize (c))
      return_trace (false);

B
Behdad Esfahbod 已提交
702
    const Chain *chain = chains;
703 704 705 706 707
    unsigned int count = chainCount;
    for (unsigned int i = 0; i < count; i++)
    {
      if (!chain->sanitize (c, version.major))
	return_trace (false);
B
Behdad Esfahbod 已提交
708
      chain = &StructAfter<Chain> (*chain);
709 710 711 712 713 714 715 716
    }

    return_trace (true);
  }

  protected:
  FixedVersion<>version;	/* Version number of the glyph metamorphosis table.
				 * 1 for mort, 2 or 3 for morx. */
B
Behdad Esfahbod 已提交
717
  HBUINT32	chainCount;	/* Number of metamorphosis chains contained in this
718
				 * table. */
B
Behdad Esfahbod 已提交
719
  Chain		chains[VAR];	/* Chains. */
720 721 722 723 724 725 726 727 728

  public:
  DEFINE_SIZE_MIN (8);
};

} /* namespace AAT */


#endif /* HB_AAT_LAYOUT_MORX_TABLE_HH */