hb-aat-layout-morx-table.hh 18.6 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
{
43
  struct driver_context_t
44
  {
45
    static const bool in_place = true;
46
    enum Flags {
B
Behdad Esfahbod 已提交
47 48
      MarkFirst		= 0x8000,	/* If set, make the current glyph the first
					 * glyph to be rearranged. */
49
      DontAdvance	= 0x4000,	/* If set, don't advance to the next glyph
B
Behdad Esfahbod 已提交
50 51 52 53 54 55 56
					 * 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. */
57 58 59 60 61 62 63
    };

    inline driver_context_t (const RearrangementSubtable *table) :
	ret (false),
	start (0), end (0),
	last_zero_before_start (0) {}

64
    inline bool transition (StateTableDriver<void> *driver,
65
			    const Entry<void> *entry)
66
    {
67
      hb_buffer_t *buffer = driver->buffer;
68 69 70
      unsigned int flags = entry->flags;

      if (flags & MarkFirst)
71
      {
72 73
	start = buffer->idx;
	last_zero_before_start = driver->last_zero;
74
      }
75

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

      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 已提交
85
	const unsigned char map[16] =
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
	{
	  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)
	{
113 114
	  buffer->unsafe_to_break (last_zero_before_start, MIN (buffer->idx + 1, buffer->len));
	  buffer->merge_clusters (start, end);
115

116
	  hb_glyph_info_t *info = buffer->info;
117
	  hb_glyph_info_t buf[4];
118

119 120 121 122
	  memcpy (buf, info + start, l * sizeof (buf[0]));
	  memcpy (buf + 2, info + end - r, r * sizeof (buf[0]));

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

	  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];
	  }
	}
      }
141 142

      return true;
143 144 145 146 147
    }

    public:
    bool ret;
    private:
B
Behdad Esfahbod 已提交
148 149 150
    unsigned int start;
    unsigned int end;
    unsigned int last_zero_before_start;
151
  };
152

153
  inline bool apply (hb_aat_apply_context_t *c) const
154 155
  {
    TRACE_APPLY (this);
156

157 158 159 160
    driver_context_t dc (this);

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

162
    return_trace (dc.ret);
163 164
  }

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

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

struct ContextualSubtable
{
179 180 181 182 183 184
  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 已提交
185
    public:
186
    DEFINE_SIZE_STATIC (4);
187 188
  };

189
  struct driver_context_t
190
  {
191
    static const bool in_place = true;
192 193 194 195 196 197 198 199 200 201 202 203 204
    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),
	mark (0),
	last_zero_before_mark (0),
	subs (table+table->substitutionTables) {}

205
    inline bool transition (StateTableDriver<EntryData> *driver,
206
			    const Entry<EntryData> *entry)
207
    {
208
      hb_buffer_t *buffer = driver->buffer;
209

210
      if (entry->data.markIndex != 0xFFFF && mark < buffer->len)
211
      {
212
	const Lookup<GlyphID> &lookup = subs[entry->data.markIndex];
213 214
	hb_glyph_info_t *info = buffer->info;
	const GlyphID *replacement = lookup.get_value (info[mark].codepoint, driver->num_glyphs);
215 216
	if (replacement)
	{
217
	  buffer->unsafe_to_break (last_zero_before_mark, MIN (buffer->idx + 1, buffer->len));
218 219 220 221
	  info[mark].codepoint = *replacement;
	  ret = true;
	}
      }
222
      if (entry->data.currentIndex != 0xFFFF && buffer->idx < buffer->len)
223
      {
224
	const Lookup<GlyphID> &lookup = subs[entry->data.currentIndex];
225 226
	hb_glyph_info_t *info = buffer->info;
	const GlyphID *replacement = lookup.get_value (info[buffer->idx].codepoint, driver->num_glyphs);
227 228
	if (replacement)
	{
229 230
	  buffer->unsafe_to_break (driver->last_zero, MIN (buffer->idx + 1, buffer->len));
	  info[buffer->idx].codepoint = *replacement;
231 232 233
	  ret = true;
	}
      }
234

235 236 237 238 239 240
      if (entry->flags & SetMark)
      {
	mark = buffer->idx;
	last_zero_before_mark = driver->last_zero;
      }

241
      return true;
242
    }
243

244 245 246 247 248 249 250
    public:
    bool ret;
    private:
    unsigned int mark;
    unsigned int last_zero_before_mark;
    const UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT32> &subs;
  };
251

252
  inline bool apply (hb_aat_apply_context_t *c) const
253 254
  {
    TRACE_APPLY (this);
255

256 257 258 259 260 261
    driver_context_t dc (this);

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

    return_trace (dc.ret);
262 263
  }

264 265 266
  inline bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
267 268 269 270 271 272 273 274 275 276 277

    unsigned int num_entries;
    if (unlikely (!machine.sanitize (c, &num_entries))) return false;

    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;

278 279 280 281
      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);
282 283 284
    }

    return_trace (substitutionTables.sanitize (c, this, num_lookups));
285
  }
286 287

  protected:
B
Behdad Esfahbod 已提交
288 289 290
  StateTable<EntryData>	machine;
  OffsetTo<UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT32>, HBUINT32>
			substitutionTables;
291
  public:
B
Behdad Esfahbod 已提交
292
  DEFINE_SIZE_STATIC (20);
293 294 295 296
};

struct LigatureSubtable
{
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
  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. */
    };
318 319 320 321 322 323 324 325 326 327
    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. */
    };
328

329 330 331 332 333 334 335 336
    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) {}
337

338
    inline bool transition (StateTableDriver<EntryData> *driver,
339 340 341
			    const Entry<EntryData> *entry)
    {
      hb_buffer_t *buffer = driver->buffer;
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
      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 已提交
367
	  buffer->move_to (match_positions[--match_length]);
368

369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
	  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 已提交
389
	    match_positions[match_length++] = buffer->out_len;
390 391 392 393 394 395 396 397 398
	    buffer->replace_glyph (lig);

	    //ligature_idx = 0; // XXX Yes or no?
	  }
	  else
	  {
	    buffer->skip_glyph ();
	    end--;
	  }
B
Behdad Esfahbod 已提交
399
	  /* TODO merge_clusters / unsafe_to_break */
400 401 402 403 404 405 406 407

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

      return true;
408 409 410 411 412
    }

    public:
    bool ret;
    private:
413 414 415 416 417 418
    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];
419 420
  };

421
  inline bool apply (hb_aat_apply_context_t *c) const
422 423
  {
    TRACE_APPLY (this);
424

425
    driver_context_t dc (this, c);
426 427 428 429 430

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

    return_trace (dc.ret);
431 432
  }

433 434 435
  inline bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
436 437 438
    /* The rest of array sanitizations are done at run-time. */
    return c->check_struct (this) && machine.sanitize (c) &&
	   ligAction && component && ligature;
439
    return_trace (true);
440
  }
441 442 443 444 445 446 447 448 449 450 451

  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);
452 453 454 455
};

struct NoncontextualSubtable
{
456
  inline bool apply (hb_aat_apply_context_t *c) const
457 458
  {
    TRACE_APPLY (this);
459

460
    bool ret = false;
461 462 463 464
    unsigned int num_glyphs = c->face->get_num_glyphs ();

    hb_glyph_info_t *info = c->buffer->info;
    unsigned int count = c->buffer->len;
465 466 467 468 469 470 471 472 473
    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;
      }
    }
474

475 476 477
    return_trace (ret);
  }

478 479 480
  inline bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
B
Behdad Esfahbod 已提交
481
    return_trace (substitute.sanitize (c));
482
  }
B
Behdad Esfahbod 已提交
483 484 485 486 487

  protected:
  Lookup<GlyphID>	substitute;
  public:
  DEFINE_SIZE_MIN (2);
488 489 490 491
};

struct InsertionSubtable
{
492
  inline bool apply (hb_aat_apply_context_t *c) const
493 494 495 496 497 498
  {
    TRACE_APPLY (this);
    /* TODO */
    return_trace (false);
  }

499 500 501 502
  inline bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    /* TODO */
503
    return_trace (true);
504 505 506 507 508 509 510 511 512 513 514 515 516
  }
};


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

  public:
B
Behdad Esfahbod 已提交
517 518 519
  HBUINT16	featureType;	/* The type of feature. */
  HBUINT16	featureSetting;	/* The feature's setting (aka selector). */
  HBUINT32	enableFlags;	/* Flags for the settings that this feature
520
				 * and setting enables. */
B
Behdad Esfahbod 已提交
521
  HBUINT32	disableFlags;	/* Complement of flags for the settings that this
522 523 524 525 526 527 528 529 530
				 * feature and setting disable. */

  public:
  DEFINE_SIZE_STATIC (12);
};


struct ChainSubtable
{
B
Behdad Esfahbod 已提交
531
  friend struct Chain;
532 533 534 535 536 537 538 539 540 541 542 543

  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
  };

544
  inline void apply (hb_aat_apply_context_t *c) const
545
  {
546
    dispatch (c);
547 548
  }

549
  template <typename context_t>
550
  inline typename context_t::return_t dispatch (context_t *c) const
551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571
  {
    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);

572
    return_trace (dispatch (c));
573 574 575
  }

  protected:
B
Behdad Esfahbod 已提交
576 577 578
  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. */
579
  union {
B
Behdad Esfahbod 已提交
580 581 582 583 584
  RearrangementSubtable		rearrangement;
  ContextualSubtable		contextual;
  LigatureSubtable		ligature;
  NoncontextualSubtable		noncontextual;
  InsertionSubtable		insertion;
585 586
  } u;
  public:
B
Behdad Esfahbod 已提交
587
  DEFINE_SIZE_MIN (12);
588 589 590 591
};

struct Chain
{
592
  inline void apply (hb_aat_apply_context_t *c) const
593
  {
B
Behdad Esfahbod 已提交
594
    const ChainSubtable *subtable = &StructAtOffset<ChainSubtable> (featureZ, featureZ[0].static_size * featureCount);
595 596 597
    unsigned int count = subtableCount;
    for (unsigned int i = 0; i < count; i++)
    {
598
      subtable->apply (c);
B
Behdad Esfahbod 已提交
599
      subtable = &StructAfter<ChainSubtable> (*subtable);
600 601
    }
  }
602 603 604 605 606 607 608 609 610 611 612 613 614 615

  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 已提交
616
    const ChainSubtable *subtable = &StructAtOffset<ChainSubtable> (featureZ, featureZ[0].static_size * featureCount);
617 618 619 620 621
    unsigned int count = subtableCount;
    for (unsigned int i = 0; i < count; i++)
    {
      if (!subtable->sanitize (c))
	return_trace (false);
B
Behdad Esfahbod 已提交
622
      subtable = &StructAfter<ChainSubtable> (*subtable);
623 624 625 626 627 628
    }

    return_trace (true);
  }

  protected:
B
Behdad Esfahbod 已提交
629 630
  HBUINT32	defaultFlags;	/* The default specification for subtables. */
  HBUINT32	length;		/* Total byte count, including this header. */
B
Behdad Esfahbod 已提交
631 632
  HBUINT32	featureCount;	/* Number of feature subtable entries. */
  HBUINT32	subtableCount;	/* The number of subtables in the chain. */
633

B
Behdad Esfahbod 已提交
634 635
  Feature	featureZ[VAR];	/* Features. */
  ChainSubtable	subtableX[VAR];	/* Subtables. */
636 637 638
  // subtableGlyphCoverageArray if major == 3

  public:
B
Behdad Esfahbod 已提交
639
  DEFINE_SIZE_MIN (16);
640 641 642 643 644 645 646
};


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

B
Behdad Esfahbod 已提交
647
struct morx
648
{
B
Behdad Esfahbod 已提交
649
  static const hb_tag_t tableTag = HB_AAT_TAG_MORX;
650

651
  inline void apply (hb_aat_apply_context_t *c) const
652
  {
B
Behdad Esfahbod 已提交
653
    const Chain *chain = chains;
654 655 656
    unsigned int count = chainCount;
    for (unsigned int i = 0; i < count; i++)
    {
657
      chain->apply (c);
B
Behdad Esfahbod 已提交
658
      chain = &StructAfter<Chain> (*chain);
659 660 661
    }
  }

662 663 664 665
  inline bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    if (!version.sanitize (c) ||
B
Behdad Esfahbod 已提交
666
	(version.major >> (sizeof (HBUINT32) == 4 ? 1 : 0)) != 1 ||
667 668 669
	!chainCount.sanitize (c))
      return_trace (false);

B
Behdad Esfahbod 已提交
670
    const Chain *chain = chains;
671 672 673 674 675
    unsigned int count = chainCount;
    for (unsigned int i = 0; i < count; i++)
    {
      if (!chain->sanitize (c, version.major))
	return_trace (false);
B
Behdad Esfahbod 已提交
676
      chain = &StructAfter<Chain> (*chain);
677 678 679 680 681 682 683 684
    }

    return_trace (true);
  }

  protected:
  FixedVersion<>version;	/* Version number of the glyph metamorphosis table.
				 * 1 for mort, 2 or 3 for morx. */
B
Behdad Esfahbod 已提交
685
  HBUINT32	chainCount;	/* Number of metamorphosis chains contained in this
686
				 * table. */
B
Behdad Esfahbod 已提交
687
  Chain		chains[VAR];	/* Chains. */
688 689 690 691 692 693 694 695 696

  public:
  DEFINE_SIZE_MIN (8);
};

} /* namespace AAT */


#endif /* HB_AAT_LAYOUT_MORX_TABLE_HH */