hb-ot-layout-gsub-private.h 20.9 KB
Newer Older
B
Behdad Esfahbod 已提交
1
/*
2
 * Copyright (C) 2007,2008,2009  Red Hat, Inc.
B
Behdad Esfahbod 已提交
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
 *
 *  This is part of HarfBuzz, an OpenType Layout engine 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.
 *
 * Red Hat Author(s): Behdad Esfahbod
 */

#ifndef HB_OT_LAYOUT_GSUB_PRIVATE_H
#define HB_OT_LAYOUT_GSUB_PRIVATE_H

30
#include "hb-ot-layout-gsubgpos-private.h"
B
Behdad Esfahbod 已提交
31

B
Behdad Esfahbod 已提交
32 33 34 35 36 37 38 39 40 41
/* XXX */
#include "harfbuzz-impl.h"
HB_INTERNAL HB_Error
_hb_buffer_add_output_glyph_ids( HB_Buffer  buffer,
			      HB_UShort  num_in,
			      HB_UShort  num_out,
			      const GlyphID *glyph_data,
			      HB_UShort  component,
			      HB_UShort  ligID );

42

B
Behdad Esfahbod 已提交
43
struct SingleSubstFormat1 {
B
Behdad Esfahbod 已提交
44 45 46 47

  friend struct SingleSubst;

  private:
B
Behdad Esfahbod 已提交
48

49
  inline bool single_substitute (hb_codepoint_t &glyph_id) const {
B
Behdad Esfahbod 已提交
50

B
Behdad Esfahbod 已提交
51
    unsigned int index = (this+coverage) (glyph_id);
B
Behdad Esfahbod 已提交
52
    if (HB_LIKELY (index == NOT_COVERED))
B
Behdad Esfahbod 已提交
53 54 55 56 57
      return false;

    glyph_id += deltaGlyphID;

    return true;
58
  }
B
Behdad Esfahbod 已提交
59

B
Behdad Esfahbod 已提交
60
  private:
B
Behdad Esfahbod 已提交
61
  USHORT	format;			/* Format identifier--format = 1 */
B
Behdad Esfahbod 已提交
62 63
  OffsetTo<Coverage>
		coverage;		/* Offset to Coverage table--from
B
Behdad Esfahbod 已提交
64 65 66 67 68 69 70
					 * beginning of Substitution table */
  SHORT		deltaGlyphID;		/* Add to original GlyphID to get
					 * substitute GlyphID */
};
ASSERT_SIZE (SingleSubstFormat1, 6);

struct SingleSubstFormat2 {
71 72 73 74 75 76 77

  friend struct SingleSubst;

  private:

  inline bool single_substitute (hb_codepoint_t &glyph_id) const {

B
Behdad Esfahbod 已提交
78
    unsigned int index = (this+coverage) (glyph_id);
B
Behdad Esfahbod 已提交
79
    if (HB_LIKELY (index == NOT_COVERED))
80
      return false;
81

B
Behdad Esfahbod 已提交
82
    if (HB_UNLIKELY (index >= substitute.len))
83 84 85 86 87
      return false;

    glyph_id = substitute[index];
    return true;
  }
B
Behdad Esfahbod 已提交
88 89

  private:
B
Behdad Esfahbod 已提交
90
  USHORT	format;			/* Format identifier--format = 2 */
B
Behdad Esfahbod 已提交
91 92
  OffsetTo<Coverage>
		coverage;		/* Offset to Coverage table--from
B
Behdad Esfahbod 已提交
93
					 * beginning of Substitution table */
B
Behdad Esfahbod 已提交
94 95 96
  ArrayOf<GlyphID>
		substitute;		/* Array of substitute
					 * GlyphIDs--ordered by Coverage Index */
B
Behdad Esfahbod 已提交
97 98 99
};
ASSERT_SIZE (SingleSubstFormat2, 6);

100 101 102 103
struct SingleSubst {

  friend struct SubstLookupSubTable;

104 105
  private:

B
Behdad Esfahbod 已提交
106
  inline bool single_substitute (hb_codepoint_t &glyph_id) const {
B
Behdad Esfahbod 已提交
107 108 109
    switch (u.format) {
    case 1: return u.format1->single_substitute (glyph_id);
    case 2: return u.format2->single_substitute (glyph_id);
B
Behdad Esfahbod 已提交
110 111 112 113
    default:return false;
    }
  }

114
  inline bool substitute (LOOKUP_ARGS_DEF) const {
115

116
    hb_codepoint_t glyph_id = IN_CURGLYPH ();
117

B
Behdad Esfahbod 已提交
118 119
    if (!single_substitute (glyph_id))
      return false;
120

B
Behdad Esfahbod 已提交
121
    _hb_buffer_replace_glyph (buffer, glyph_id);
122 123 124 125 126 127 128 129 130 131 132 133

    if ( _hb_ot_layout_has_new_glyph_classes (layout) )
    {
      /* we inherit the old glyph class to the substituted glyph */
      _hb_ot_layout_set_glyph_property (layout, glyph_id, property);
    }

    return true;
  }

  private:
  union {
B
Behdad Esfahbod 已提交
134 135 136
  USHORT		format;		/* Format identifier */
  SingleSubstFormat1	format1[];
  SingleSubstFormat2	format2[];
137
  } u;
B
Behdad Esfahbod 已提交
138
};
B
Behdad Esfahbod 已提交
139
ASSERT_SIZE (SingleSubst, 2);
140

B
Behdad Esfahbod 已提交
141 142

struct Sequence {
143 144 145 146 147 148 149

  friend struct MultipleSubstFormat1;

  private:

  inline void set_glyph_class (hb_ot_layout_t *layout, unsigned int property) const {

B
Behdad Esfahbod 已提交
150
    unsigned int count = substitute.len;
151
    for (unsigned int n = 0; n < count; n++)
152 153
      _hb_ot_layout_set_glyph_property (layout, substitute[n], property);
  }
B
Behdad Esfahbod 已提交
154

155
  inline bool substitute_sequence (LOOKUP_ARGS_DEF) const {
B
Behdad Esfahbod 已提交
156

B
Behdad Esfahbod 已提交
157
    if (HB_UNLIKELY (!substitute.len))
B
Behdad Esfahbod 已提交
158 159 160
      return false;

    _hb_buffer_add_output_glyph_ids (buffer, 1,
B
Behdad Esfahbod 已提交
161
				     substitute.len, substitute.array,
B
Behdad Esfahbod 已提交
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
				     0xFFFF, 0xFFFF);

    if ( _hb_ot_layout_has_new_glyph_classes (layout) )
    {
      /* this is a guess only ... */

      if ( property == HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE )
        property = HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH;

      set_glyph_class (layout, property);
    }

    return true;
  }

B
Behdad Esfahbod 已提交
177
  private:
B
Behdad Esfahbod 已提交
178 179
  ArrayOf<GlyphID>
		substitute;		/* String of GlyphIDs to substitute */
B
Behdad Esfahbod 已提交
180
};
181
ASSERT_SIZE (Sequence, 2);
B
Behdad Esfahbod 已提交
182

183 184 185 186 187 188
struct MultipleSubstFormat1 {

  friend struct MultipleSubst;

  private:

189
  inline bool substitute (LOOKUP_ARGS_DEF) const {
190

B
Behdad Esfahbod 已提交
191
    unsigned int index = (this+coverage) (IN_CURGLYPH ());
B
Behdad Esfahbod 已提交
192
    if (HB_LIKELY (index == NOT_COVERED))
193 194
      return false;

195
    return (this+sequence[index]).substitute_sequence (LOOKUP_ARGS);
196
  }
B
Behdad Esfahbod 已提交
197 198

  private:
B
Behdad Esfahbod 已提交
199
  USHORT	format;			/* Format identifier--format = 1 */
B
Behdad Esfahbod 已提交
200 201
  OffsetTo<Coverage>
		coverage;		/* Offset to Coverage table--from
B
Behdad Esfahbod 已提交
202
					 * beginning of Substitution table */
B
Behdad Esfahbod 已提交
203 204 205
  OffsetArrayOf<Sequence>
		sequence;		/* Array of Sequence tables
					 * ordered by Coverage Index */
B
Behdad Esfahbod 已提交
206
};
207 208 209 210
ASSERT_SIZE (MultipleSubstFormat1, 6);

struct MultipleSubst {

211 212 213 214
  friend struct SubstLookupSubTable;

  private:

215
  inline bool substitute (LOOKUP_ARGS_DEF) const {
B
Behdad Esfahbod 已提交
216 217
    switch (u.format) {
    case 1: return u.format1->substitute (LOOKUP_ARGS);
218 219 220 221 222 223
    default:return false;
    }
  }

  private:
  union {
B
Behdad Esfahbod 已提交
224 225
  USHORT		format;		/* Format identifier */
  MultipleSubstFormat1	format1[];
226 227
  } u;
};
B
Behdad Esfahbod 已提交
228
ASSERT_SIZE (MultipleSubst, 2);
229

B
Behdad Esfahbod 已提交
230

B
Behdad Esfahbod 已提交
231
typedef ArrayOf<GlyphID> AlternateSet;	/* Array of alternate GlyphIDs--in
B
Behdad Esfahbod 已提交
232
					 * arbitrary order */
233
ASSERT_SIZE (AlternateSet, 2);
B
Behdad Esfahbod 已提交
234

235
struct AlternateSubstFormat1 {
236 237 238 239 240

  friend struct AlternateSubst;

  private:

241
  inline bool substitute (LOOKUP_ARGS_DEF) const {
242

243
    hb_codepoint_t glyph_id = IN_CURGLYPH ();
244

B
Behdad Esfahbod 已提交
245
    unsigned int index = (this+coverage) (glyph_id);
B
Behdad Esfahbod 已提交
246
    if (HB_LIKELY (index == NOT_COVERED))
247 248
      return false;

B
Behdad Esfahbod 已提交
249
    const AlternateSet &alt_set = this+alternateSet[index];
250

B
Behdad Esfahbod 已提交
251
    if (HB_UNLIKELY (!alt_set.len))
252 253 254 255 256
      return false;

    unsigned int alt_index = 0;

    /* XXX callback to user to choose alternate
B
Behdad Esfahbod 已提交
257 258 259 260
    if (layout->altfunc)
      alt_index = (layout->altfunc)(layout, buffer,
				    buffer->out_pos, glyph_id,
				    alt_set.len, alt_set.array);
261 262
				   */

B
Behdad Esfahbod 已提交
263
    if (HB_UNLIKELY (alt_index >= alt_set.len))
264 265 266 267
      return false;

    glyph_id = alt_set[alt_index];

B
Behdad Esfahbod 已提交
268
    _hb_buffer_replace_glyph (buffer, glyph_id);
269 270 271 272 273 274 275 276 277

    if ( _hb_ot_layout_has_new_glyph_classes (layout) )
    {
      /* we inherit the old glyph class to the substituted glyph */
      _hb_ot_layout_set_glyph_property (layout, glyph_id, property);
    }

    return true;
  }
B
Behdad Esfahbod 已提交
278 279

  private:
B
Behdad Esfahbod 已提交
280
  USHORT	format;			/* Format identifier--format = 1 */
B
Behdad Esfahbod 已提交
281 282
  OffsetTo<Coverage>
		coverage;		/* Offset to Coverage table--from
B
Behdad Esfahbod 已提交
283
					 * beginning of Substitution table */
B
Behdad Esfahbod 已提交
284 285 286
  OffsetArrayOf<AlternateSet>
		alternateSet;		/* Array of AlternateSet tables
					 * ordered by Coverage Index */
B
Behdad Esfahbod 已提交
287
};
288 289
ASSERT_SIZE (AlternateSubstFormat1, 6);

290 291 292 293 294 295
struct AlternateSubst {

  friend struct SubstLookupSubTable;

  private:

296
  inline bool substitute (LOOKUP_ARGS_DEF) const {
B
Behdad Esfahbod 已提交
297 298
    switch (u.format) {
    case 1: return u.format1->substitute (LOOKUP_ARGS);
299 300 301 302 303 304
    default:return false;
    }
  }

  private:
  union {
B
Behdad Esfahbod 已提交
305 306
  USHORT		format;		/* Format identifier */
  AlternateSubstFormat1	format1[];
307 308
  } u;
};
B
Behdad Esfahbod 已提交
309
ASSERT_SIZE (AlternateSubst, 2);
310

311 312

struct Ligature {
313 314 315 316

  friend struct LigatureSet;

  private:
B
Behdad Esfahbod 已提交
317
  inline bool substitute_ligature (LOOKUP_ARGS_DEF, bool is_mark) const {
318 319

    unsigned int i, j;
B
Behdad Esfahbod 已提交
320
    unsigned int count = component.len;
321 322 323
    unsigned int end = MIN (buffer->in_length, buffer->in_pos + context_length);
    if (HB_UNLIKELY (buffer->in_pos + count > end))
      return false;
324 325 326

    for (i = 1, j = buffer->in_pos + 1; i < count; i++, j++) {
      while (!_hb_ot_layout_check_glyph_property (layout, IN_ITEM (j), lookup_flag, &property)) {
327
	if (HB_UNLIKELY (j + count - i == end))
328 329 330 331 332 333 334 335
	  return false;
	j++;
      }

      if (!(property == HB_OT_LAYOUT_GLYPH_CLASS_MARK ||
	    property &  LookupFlag::MarkAttachmentType))
	is_mark = FALSE;

B
Behdad Esfahbod 已提交
336
      if (HB_LIKELY (IN_GLYPH(j) != component[i]))
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
        return false;
    }
    if ( _hb_ot_layout_has_new_glyph_classes (layout) )
      /* this is just a guess ... */
      hb_ot_layout_set_glyph_class (layout, ligGlyph,
				    is_mark ? HB_OT_LAYOUT_GLYPH_CLASS_MARK
					    : HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE);

    if (j == buffer->in_pos + i) /* No input glyphs skipped */
      /* We don't use a new ligature ID if there are no skipped
	 glyphs and the ligature already has an ID. */
      _hb_buffer_add_output_glyph_ids (buffer, i,
				       1, &ligGlyph,
				       0xFFFF,
				       IN_LIGID (buffer->in_pos) ?
				       0xFFFF : _hb_buffer_allocate_ligid (buffer));
    else
    {
      unsigned int lig_id = _hb_buffer_allocate_ligid (buffer);
      _hb_buffer_add_output_glyph (buffer, ligGlyph, 0xFFFF, lig_id);

      /* Now we must do a second loop to copy the skipped glyphs to
	 `out' and assign component values to it.  We start with the
	 glyph after the first component.  Glyphs between component
	 i and i+1 belong to component i.  Together with the lig_id
	 value it is later possible to check whether a specific
	 component value really belongs to a given ligature. */

B
Behdad Esfahbod 已提交
365
      for ( i = 1; i < count; i++ )
366 367
      {
	while (!_hb_ot_layout_check_glyph_property (layout, IN_CURITEM(), lookup_flag, &property))
B
Behdad Esfahbod 已提交
368
	  _hb_buffer_add_output_glyph (buffer, IN_CURGLYPH(), i - 1, lig_id);
369 370 371 372

	(buffer->in_pos)++;
      }

B
Behdad Esfahbod 已提交
373
      /* TODO We should possibly reassign lig_id and component for any
374 375 376 377 378 379
       * components of a previous ligature that s now being removed as part of
       * this ligature. */
    }

    return true;
  }
380 381 382

  private:
  GlyphID	ligGlyph;		/* GlyphID of ligature to substitute */
B
Behdad Esfahbod 已提交
383 384
  HeadlessArrayOf<GlyphID>
		component;		/* Array of component GlyphIDs--start
385 386 387
					 * with the second  component--ordered
					 * in writing direction */
};
388
ASSERT_SIZE (Ligature, 4);
B
Behdad Esfahbod 已提交
389 390

struct LigatureSet {
391 392 393 394 395

  friend struct LigatureSubstFormat1;

  private:

B
Behdad Esfahbod 已提交
396
  inline bool substitute_ligature (LOOKUP_ARGS_DEF, bool is_mark) const {
397

B
Behdad Esfahbod 已提交
398
    unsigned int num_ligs = ligature.len;
399
    for (unsigned int i = 0; i < num_ligs; i++) {
B
Behdad Esfahbod 已提交
400
      const Ligature &lig = this+ligature[i];
B
Behdad Esfahbod 已提交
401
      if (lig.substitute_ligature (LOOKUP_ARGS, is_mark))
402 403 404 405 406
        return true;
    }

    return false;
  }
B
Behdad Esfahbod 已提交
407 408

  private:
B
Behdad Esfahbod 已提交
409 410 411
  OffsetArrayOf<Ligature>
		ligature;		/* Array LigatureSet tables
					 * ordered by preference */
B
Behdad Esfahbod 已提交
412
};
413
ASSERT_SIZE (LigatureSet, 2);
B
Behdad Esfahbod 已提交
414

415
struct LigatureSubstFormat1 {
416 417 418 419 420

  friend struct LigatureSubst;

  private:

421
  inline bool substitute (LOOKUP_ARGS_DEF) const {
422 423 424 425 426 427

    hb_codepoint_t glyph_id = IN_CURGLYPH ();

    bool first_is_mark = (property == HB_OT_LAYOUT_GLYPH_CLASS_MARK ||
			  property &  LookupFlag::MarkAttachmentType);

B
Behdad Esfahbod 已提交
428
    unsigned int index = (this+coverage) (glyph_id);
B
Behdad Esfahbod 已提交
429
    if (HB_LIKELY (index == NOT_COVERED))
430 431
      return false;

B
Behdad Esfahbod 已提交
432
    const LigatureSet &lig_set = this+ligatureSet[index];
B
Behdad Esfahbod 已提交
433
    return lig_set.substitute_ligature (LOOKUP_ARGS, first_is_mark);
434
  }
B
Behdad Esfahbod 已提交
435 436

  private:
B
Behdad Esfahbod 已提交
437
  USHORT	format;			/* Format identifier--format = 1 */
B
Behdad Esfahbod 已提交
438 439
  OffsetTo<Coverage>
		coverage;		/* Offset to Coverage table--from
440
					 * beginning of Substitution table */
B
Behdad Esfahbod 已提交
441 442 443
  OffsetArrayOf<LigatureSet>\
		ligatureSet;		/* Array LigatureSet tables
					 * ordered by Coverage Index */
B
Behdad Esfahbod 已提交
444
};
445 446
ASSERT_SIZE (LigatureSubstFormat1, 6);

447 448 449 450 451 452
struct LigatureSubst {

  friend struct SubstLookupSubTable;

  private:

453
  inline bool substitute (LOOKUP_ARGS_DEF) const {
B
Behdad Esfahbod 已提交
454 455
    switch (u.format) {
    case 1: return u.format1->substitute (LOOKUP_ARGS);
456 457 458 459 460 461
    default:return false;
    }
  }

  private:
  union {
B
Behdad Esfahbod 已提交
462 463
  USHORT		format;		/* Format identifier */
  LigatureSubstFormat1	format1[];
464 465
  } u;
};
B
Behdad Esfahbod 已提交
466
ASSERT_SIZE (LigatureSubst, 2);
467

B
Behdad Esfahbod 已提交
468

469

470
static inline bool substitute_lookup (LOOKUP_ARGS_DEF, unsigned int lookup_index);
B
Behdad Esfahbod 已提交
471

472

473
struct ContextSubst : Context {
B
Behdad Esfahbod 已提交
474

475
  inline bool substitute (LOOKUP_ARGS_DEF) const {
476
    return this->apply (LOOKUP_ARGS, substitute_lookup);
477 478
  }
};
B
Behdad Esfahbod 已提交
479
ASSERT_SIZE (ContextSubst, 2);
480 481


482
struct ChainContextSubst : ChainContext {
B
Behdad Esfahbod 已提交
483

484
  inline bool substitute (LOOKUP_ARGS_DEF) const {
485
    return this->apply (LOOKUP_ARGS, substitute_lookup);
B
Behdad Esfahbod 已提交
486 487
  }
};
B
Behdad Esfahbod 已提交
488
ASSERT_SIZE (ChainContextSubst, 2);
B
Behdad Esfahbod 已提交
489

490

B
Behdad Esfahbod 已提交
491
struct ExtensionSubstFormat1 {
492 493 494 495 496

  friend struct ExtensionSubst;

  private:
  inline unsigned int get_type (void) const { return extensionLookupType; }
B
Behdad Esfahbod 已提交
497
  inline unsigned int get_offset (void) const { return (extensionOffset[0] << 16) + extensionOffset[1]; }
498
  inline bool substitute (LOOKUP_ARGS_DEF) const;
B
Behdad Esfahbod 已提交
499 500

  private:
B
Behdad Esfahbod 已提交
501
  USHORT	format;			/* Format identifier. Set to 1. */
B
Behdad Esfahbod 已提交
502 503 504
  USHORT	extensionLookupType;	/* Lookup type of subtable referenced
					 * by ExtensionOffset (i.e. the
					 * extension subtable). */
B
Behdad Esfahbod 已提交
505 506 507 508
  USHORT	extensionOffset[2];	/* Offset to the extension subtable,
					 * of lookup type subtable.
					 * Defined as two shorts to avoid
					 * alignment requirements. */
B
Behdad Esfahbod 已提交
509 510 511
};
ASSERT_SIZE (ExtensionSubstFormat1, 8);

512 513 514 515 516 517 518 519
struct ExtensionSubst {

  friend struct SubstLookup;
  friend struct SubstLookupSubTable;

  private:

  inline unsigned int get_type (void) const {
B
Behdad Esfahbod 已提交
520 521
    switch (u.format) {
    case 1: return u.format1->get_type ();
522 523 524 525
    default:return 0;
    }
  }

526
  inline bool substitute (LOOKUP_ARGS_DEF) const {
B
Behdad Esfahbod 已提交
527 528
    switch (u.format) {
    case 1: return u.format1->substitute (LOOKUP_ARGS);
529 530 531 532 533 534
    default:return false;
    }
  }

  private:
  union {
B
Behdad Esfahbod 已提交
535 536
  USHORT		format;		/* Format identifier */
  ExtensionSubstFormat1	format1[];
537 538
  } u;
};
B
Behdad Esfahbod 已提交
539
ASSERT_SIZE (ExtensionSubst, 2);
540 541


B
Behdad Esfahbod 已提交
542
struct ReverseChainSingleSubstFormat1 {
543 544 545 546

  friend struct ReverseChainSingleSubst;

  private:
547
  inline bool substitute (LOOKUP_ARGS_DEF) const {
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573

    if (HB_UNLIKELY (context_length != NO_CONTEXT))
      return false; /* No chaining to this type */

    unsigned int index = (this+coverage) (IN_CURGLYPH ());
    if (HB_LIKELY (index == NOT_COVERED))
      return false;

    const OffsetArrayOf<Coverage> &lookahead = * (const OffsetArrayOf<Coverage> *)
						 ((const char *) &backtrack + backtrack.get_size ());
    const ArrayOf<GlyphID> &substitute = * (const ArrayOf<GlyphID> *)
					   ((const char *) &lookahead + lookahead.get_size ());

    if (match_backtrack (LOOKUP_ARGS,
			 backtrack.len, (USHORT *) backtrack.array,
			 match_coverage, (char *) this) &&
        match_lookahead (LOOKUP_ARGS,
			 lookahead.len, (USHORT *) lookahead.array,
			 match_coverage, (char *) this,
			 1))
    {
      IN_CURGLYPH() = substitute[index];
      buffer->in_pos--; /* Reverse! */
      return true;
    }

574 575
    return false;
  }
B
Behdad Esfahbod 已提交
576 577

  private:
B
Behdad Esfahbod 已提交
578
  USHORT	format;			/* Format identifier--format = 1 */
579 580 581 582 583
  OffsetTo<Coverage>
		coverage;		/* Offset to Coverage table--from
					 * beginning of table */
  OffsetArrayOf<Coverage>
		backtrack;		/* Array of coverage tables
B
Behdad Esfahbod 已提交
584 585
					 * in backtracking sequence, in  glyph
					 * sequence order */
586 587 588
  OffsetArrayOf<Coverage>
		lookaheadX;		/* Array of coverage tables
					 * in lookahead sequence, in glyph
B
Behdad Esfahbod 已提交
589
					 * sequence order */
590 591 592
  ArrayOf<GlyphID>
		substituteX;		/* Array of substitute
					 * GlyphIDs--ordered by Coverage Index */
B
Behdad Esfahbod 已提交
593 594 595
};
ASSERT_SIZE (ReverseChainSingleSubstFormat1, 10);

596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618
struct ReverseChainSingleSubst {

  friend struct SubstLookupSubTable;

  private:

  inline bool substitute (LOOKUP_ARGS_DEF) const {
    switch (u.format) {
    case 1: return u.format1->substitute (LOOKUP_ARGS);
    default:return false;
    }
  }

  private:
  union {
  USHORT				format;		/* Format identifier */
  ReverseChainSingleSubstFormat1	format1[];
  } u;
};
ASSERT_SIZE (ReverseChainSingleSubst, 2);



B
Behdad Esfahbod 已提交
619 620 621 622
/*
 * SubstLookup
 */

623
enum {
624 625 626 627 628 629 630 631
  GSUB_Single			= 1,
  GSUB_Multiple			= 2,
  GSUB_Alternate		= 3,
  GSUB_Ligature			= 4,
  GSUB_Context			= 5,
  GSUB_ChainContext		= 6,
  GSUB_Extension		= 7,
  GSUB_ReverseChainSingle	= 8,
632 633
};

B
Behdad Esfahbod 已提交
634 635 636 637
struct SubstLookupSubTable {

  friend struct SubstLookup;

B
Behdad Esfahbod 已提交
638
  inline bool substitute (LOOKUP_ARGS_DEF,
639
			  unsigned int lookup_type) const {
640

641
    switch (lookup_type) {
642 643 644 645 646 647 648 649
    case GSUB_Single:			return u.single->substitute (LOOKUP_ARGS);
    case GSUB_Multiple:			return u.multiple->substitute (LOOKUP_ARGS);
    case GSUB_Alternate:		return u.alternate->substitute (LOOKUP_ARGS);
    case GSUB_Ligature:			return u.ligature->substitute (LOOKUP_ARGS);
    case GSUB_Context:			return u.context->substitute (LOOKUP_ARGS);
    case GSUB_ChainContext:		return u.chainingContext->substitute (LOOKUP_ARGS);
    case GSUB_Extension:		return u.extension->substitute (LOOKUP_ARGS);
    case GSUB_ReverseChainSingle:	return u.reverseChainContextSingle->substitute (LOOKUP_ARGS);
650 651
    default:return false;
    }
B
Behdad Esfahbod 已提交
652 653 654 655
  }

  private:
  union {
656 657 658 659 660 661 662 663 664
  USHORT			format;
  SingleSubst			single[];
  MultipleSubst			multiple[];
  AlternateSubst		alternate[];
  LigatureSubst			ligature[];
  ContextSubst			context[];
  ChainContextSubst		chainingContext[];
  ExtensionSubst		extension[];
  ReverseChainSingleSubst	reverseChainContextSingle[];
B
Behdad Esfahbod 已提交
665 666
  } u;
};
B
Behdad Esfahbod 已提交
667
ASSERT_SIZE (SubstLookupSubTable, 2);
B
Behdad Esfahbod 已提交
668

669

B
Behdad Esfahbod 已提交
670 671 672 673 674 675 676
struct SubstLookup : Lookup {

  inline const SubstLookupSubTable& get_subtable (unsigned int i) const {
    return *(SubstLookupSubTable*)&(((Lookup *)this)->get_subtable (i));
  }

  /* Like get_type(), but looks through extension lookups.
677
   * Never returns Extension */
B
Behdad Esfahbod 已提交
678 679 680
  inline unsigned int get_effective_type (void) const {
    unsigned int type = get_type ();

681
    if (HB_UNLIKELY (type == GSUB_Extension)) {
682
      unsigned int count = get_subtable_count ();
B
Behdad Esfahbod 已提交
683
      type = get_subtable(0).u.extension->get_type ();
684 685 686 687 688
      /* The spec says all subtables should have the same type.
       * This is specially important if one has a reverse type! */
      for (unsigned int i = 1; i < count; i++)
        if (get_subtable(i).u.extension->get_type () != type)
	  return 0;
B
Behdad Esfahbod 已提交
689 690 691 692 693 694 695
    }

    return type;
  }

  inline bool is_reverse (void) const {
    switch (get_effective_type ()) {
696 697
    case GSUB_ReverseChainSingle:	return true;
    default:				return false;
B
Behdad Esfahbod 已提交
698 699 700
    }
  }

701 702 703 704 705
  inline bool substitute_subtables (hb_ot_layout_t *layout,
				    hb_buffer_t    *buffer,
				    unsigned int    context_length,
				    unsigned int    nesting_level_left,
				    unsigned int    property) const {
B
Behdad Esfahbod 已提交
706
    unsigned int lookup_type = get_type ();
B
Behdad Esfahbod 已提交
707
    unsigned int lookup_flag = get_flag ();
B
Behdad Esfahbod 已提交
708 709

    for (unsigned int i = 0; i < get_subtable_count (); i++)
B
Behdad Esfahbod 已提交
710
      if (get_subtable (i).substitute (LOOKUP_ARGS,
711
				       lookup_type))
B
Behdad Esfahbod 已提交
712
	return true;
B
Behdad Esfahbod 已提交
713

B
Behdad Esfahbod 已提交
714 715
    return false;
  }
716

717 718 719 720 721 722 723 724 725 726 727 728
  inline bool substitute_once (hb_ot_layout_t *layout,
			       hb_buffer_t    *buffer) const {

    unsigned int lookup_flag = get_flag ();

    unsigned int property;
    if (!_hb_ot_layout_check_glyph_property (layout, IN_CURITEM (), lookup_flag, &property))
      return false;

    return substitute_subtables (layout, buffer, NO_CONTEXT, MAX_NESTING_LEVEL, property);
  }

729 730 731
  bool substitute_string (hb_ot_layout_t *layout,
			  hb_buffer_t    *buffer,
			  hb_ot_layout_feature_mask_t mask) const {
732 733 734

    bool ret = false;

B
Behdad Esfahbod 已提交
735 736 737
    if (HB_UNLIKELY (!buffer->in_length))
      return false;

738
    if (HB_LIKELY (!is_reverse ())) {
739 740 741 742 743 744 745

	/* in/out forward substitution */
	_hb_buffer_clear_output (buffer);
	buffer->in_pos = 0;
	while (buffer->in_pos < buffer->in_length) {

	  if ((~IN_PROPERTIES (buffer->in_pos) & mask) &&
746
	      substitute_once (layout, buffer))
747 748
	    ret = true;
	  else
B
Behdad Esfahbod 已提交
749
	    _hb_buffer_next_glyph (buffer);
750 751 752 753 754 755 756 757 758 759 760 761

	}
	if (ret)
	  _hb_buffer_swap (buffer);

    } else {

	/* in-place backward substitution */
	buffer->in_pos = buffer->in_length - 1;
	do {

	  if ((~IN_PROPERTIES (buffer->in_pos) & mask) &&
762
	      substitute_once (layout, buffer))
763 764 765 766
	    ret = true;
	  else
	    buffer->in_pos--;

767
	} while ((int) buffer->in_pos >= 0);
768 769 770 771
    }

    return ret;
  }
B
Behdad Esfahbod 已提交
772
};
B
Behdad Esfahbod 已提交
773
ASSERT_SIZE (SubstLookup, 6);
B
Behdad Esfahbod 已提交
774

775

B
Minor  
Behdad Esfahbod 已提交
776 777 778 779 780 781 782 783 784
/*
 * GSUB
 */

struct GSUB : GSUBGPOS {
  static const hb_tag_t Tag		= HB_TAG ('G','S','U','B');

  STATIC_DEFINE_GET_FOR_DATA (GSUB);
  /* XXX check version here? */
B
Behdad Esfahbod 已提交
785 786 787 788 789

  inline const SubstLookup& get_lookup (unsigned int i) const {
    return *(SubstLookup*)&(((GSUBGPOS *)this)->get_lookup (i));
  }

790 791 792 793 794 795
  inline bool substitute_lookup (hb_ot_layout_t *layout,
				 hb_buffer_t    *buffer,
			         unsigned int    lookup_index,
				 hb_ot_layout_feature_mask_t  mask) const {
    return get_lookup (lookup_index).substitute_string (layout, buffer, mask);
  }
B
Behdad Esfahbod 已提交
796

B
Minor  
Behdad Esfahbod 已提交
797
};
B
Behdad Esfahbod 已提交
798
ASSERT_SIZE (GSUB, 10);
799 800


B
Behdad Esfahbod 已提交
801
/* Out-of-class implementation for methods recursing */
802

803
inline bool ExtensionSubstFormat1::substitute (LOOKUP_ARGS_DEF) const {
804 805
  unsigned int lookup_type = get_type ();

B
Behdad Esfahbod 已提交
806
  if (HB_UNLIKELY (lookup_type ==  GSUB_Extension))
807 808
    return false;

B
Behdad Esfahbod 已提交
809
  return (*(SubstLookupSubTable *)(((char *) this) + get_offset ())).substitute (LOOKUP_ARGS,
810
										 lookup_type);
811 812
}

813
static inline bool substitute_lookup (LOOKUP_ARGS_DEF, unsigned int lookup_index) {
814
  const GSUB &gsub = *(layout->gsub);
815
  const SubstLookup &l = gsub.get_lookup (lookup_index);
816

817 818 819 820 821 822 823
  if (HB_UNLIKELY (nesting_level_left == 0))
    return false;
  nesting_level_left--;

  if (HB_UNLIKELY (context_length < 1))
    return false;

824
  return l.substitute_subtables (layout, buffer, context_length, nesting_level_left, property);
825 826 827
}


B
Behdad Esfahbod 已提交
828
#endif /* HB_OT_LAYOUT_GSUB_PRIVATE_H */