hb-aat-layout-morx-table.hh 19.2 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
    };

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

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

      if (flags & MarkFirst)
75
	start = buffer->idx;
76

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

      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 已提交
86
	const unsigned char map[16] =
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 113
	{
	  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 已提交
114
	  buffer->merge_clusters (start, MIN (buffer->idx + 1, buffer->len));
115
	  buffer->merge_clusters (start, end);
116

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

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

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

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

      return true;
144 145 146 147 148
    }

    public:
    bool ret;
    private:
B
Behdad Esfahbod 已提交
149 150
    unsigned int start;
    unsigned int end;
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
    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),
201
	mark_set (false),
202 203 204
	mark (0),
	subs (table+table->substitutionTables) {}

B
Behdad Esfahbod 已提交
205 206 207 208 209 210 211 212 213 214
    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;
    }
215
    inline bool transition (StateTableDriver<EntryData> *driver,
216
			    const Entry<EntryData> *entry)
217
    {
218
      hb_buffer_t *buffer = driver->buffer;
219

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

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

256
      return true;
257
    }
258

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

267
  inline bool apply (hb_aat_apply_context_t *c) const
268 269
  {
    TRACE_APPLY (this);
270

271 272 273 274 275 276
    driver_context_t dc (this);

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

    return_trace (dc.ret);
277 278
  }

279 280 281
  inline bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
282 283 284 285 286 287 288 289 290 291 292

    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;

293 294 295 296
      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);
297 298 299
    }

    return_trace (substitutionTables.sanitize (c, this, num_lookups));
300
  }
301 302

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

struct LigatureSubtable
{
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
  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. */
    };
333 334 335 336 337 338 339 340 341 342
    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. */
    };
343

344 345 346 347 348 349 350 351
    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) {}
352

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

389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
	  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 已提交
409
	    match_positions[match_length++] = buffer->out_len;
410 411 412 413 414 415 416 417 418
	    buffer->replace_glyph (lig);

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

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

      return true;
428 429 430 431 432
    }

    public:
    bool ret;
    private:
433 434 435 436 437 438
    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];
439 440
  };

441
  inline bool apply (hb_aat_apply_context_t *c) const
442 443
  {
    TRACE_APPLY (this);
444

445
    driver_context_t dc (this, c);
446 447 448 449 450

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

    return_trace (dc.ret);
451 452
  }

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

  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);
472 473 474 475
};

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

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

    hb_glyph_info_t *info = c->buffer->info;
    unsigned int count = c->buffer->len;
485 486 487 488 489 490 491 492 493
    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;
      }
    }
494

495 496 497
    return_trace (ret);
  }

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

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

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

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


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

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

  public:
  DEFINE_SIZE_STATIC (12);
};


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

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

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

569
  template <typename context_t>
570
  inline typename context_t::return_t dispatch (context_t *c) const
571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591
  {
    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);

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

  protected:
B
Behdad Esfahbod 已提交
596 597 598
  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. */
599
  union {
B
Behdad Esfahbod 已提交
600 601 602 603 604
  RearrangementSubtable		rearrangement;
  ContextualSubtable		contextual;
  LigatureSubtable		ligature;
  NoncontextualSubtable		noncontextual;
  InsertionSubtable		insertion;
605 606
  } u;
  public:
B
Behdad Esfahbod 已提交
607
  DEFINE_SIZE_MIN (12);
608 609 610 611
};

struct Chain
{
612
  inline void apply (hb_aat_apply_context_t *c) const
613
  {
B
Behdad Esfahbod 已提交
614
    const ChainSubtable *subtable = &StructAtOffset<ChainSubtable> (featureZ, featureZ[0].static_size * featureCount);
615 616 617
    unsigned int count = subtableCount;
    for (unsigned int i = 0; i < count; i++)
    {
618
      subtable->apply (c);
B
Behdad Esfahbod 已提交
619
      subtable = &StructAfter<ChainSubtable> (*subtable);
620 621
    }
  }
622 623 624 625 626 627 628 629 630 631 632 633 634 635

  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 已提交
636
    const ChainSubtable *subtable = &StructAtOffset<ChainSubtable> (featureZ, featureZ[0].static_size * featureCount);
637 638 639 640 641
    unsigned int count = subtableCount;
    for (unsigned int i = 0; i < count; i++)
    {
      if (!subtable->sanitize (c))
	return_trace (false);
B
Behdad Esfahbod 已提交
642
      subtable = &StructAfter<ChainSubtable> (*subtable);
643 644 645 646 647 648
    }

    return_trace (true);
  }

  protected:
B
Behdad Esfahbod 已提交
649 650
  HBUINT32	defaultFlags;	/* The default specification for subtables. */
  HBUINT32	length;		/* Total byte count, including this header. */
B
Behdad Esfahbod 已提交
651 652
  HBUINT32	featureCount;	/* Number of feature subtable entries. */
  HBUINT32	subtableCount;	/* The number of subtables in the chain. */
653

B
Behdad Esfahbod 已提交
654 655
  Feature	featureZ[VAR];	/* Features. */
  ChainSubtable	subtableX[VAR];	/* Subtables. */
656 657 658
  // subtableGlyphCoverageArray if major == 3

  public:
B
Behdad Esfahbod 已提交
659
  DEFINE_SIZE_MIN (16);
660 661 662 663 664 665 666
};


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

B
Behdad Esfahbod 已提交
667
struct morx
668
{
B
Behdad Esfahbod 已提交
669
  static const hb_tag_t tableTag = HB_AAT_TAG_MORX;
670

671
  inline void apply (hb_aat_apply_context_t *c) const
672
  {
B
Behdad Esfahbod 已提交
673
    const Chain *chain = chains;
674 675 676
    unsigned int count = chainCount;
    for (unsigned int i = 0; i < count; i++)
    {
677
      chain->apply (c);
B
Behdad Esfahbod 已提交
678
      chain = &StructAfter<Chain> (*chain);
679 680 681
    }
  }

682 683 684 685
  inline bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    if (!version.sanitize (c) ||
B
Behdad Esfahbod 已提交
686
	(version.major >> (sizeof (HBUINT32) == 4 ? 1 : 0)) != 1 ||
687 688 689
	!chainCount.sanitize (c))
      return_trace (false);

B
Behdad Esfahbod 已提交
690
    const Chain *chain = chains;
691 692 693 694 695
    unsigned int count = chainCount;
    for (unsigned int i = 0; i < count; i++)
    {
      if (!chain->sanitize (c, version.major))
	return_trace (false);
B
Behdad Esfahbod 已提交
696
      chain = &StructAfter<Chain> (*chain);
697 698 699 700 701 702 703 704
    }

    return_trace (true);
  }

  protected:
  FixedVersion<>version;	/* Version number of the glyph metamorphosis table.
				 * 1 for mort, 2 or 3 for morx. */
B
Behdad Esfahbod 已提交
705
  HBUINT32	chainCount;	/* Number of metamorphosis chains contained in this
706
				 * table. */
B
Behdad Esfahbod 已提交
707
  Chain		chains[VAR];	/* Chains. */
708 709 710 711 712 713 714 715 716

  public:
  DEFINE_SIZE_MIN (8);
};

} /* namespace AAT */


#endif /* HB_AAT_LAYOUT_MORX_TABLE_HH */