hb-ot-layout-gsub-table.hh revision 29416833584d7831ece84aaeada6f5ebba7828c0
1/*
2 * Copyright © 2007,2008,2009,2010  Red Hat, Inc.
3 * Copyright © 2010,2012  Google, Inc.
4 *
5 *  This is part of HarfBuzz, a text shaping library.
6 *
7 * Permission is hereby granted, without written agreement and without
8 * license or royalty fees, to use, copy, modify, and distribute this
9 * software and its documentation for any purpose, provided that the
10 * above copyright notice and the following two paragraphs appear in
11 * all copies of this software.
12 *
13 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
14 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
15 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
16 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
17 * DAMAGE.
18 *
19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
22 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
24 *
25 * Red Hat Author(s): Behdad Esfahbod
26 * Google Author(s): Behdad Esfahbod
27 */
28
29#ifndef HB_OT_LAYOUT_GSUB_TABLE_HH
30#define HB_OT_LAYOUT_GSUB_TABLE_HH
31
32#include "hb-ot-layout-gsubgpos-private.hh"
33
34
35namespace OT {
36
37
38struct SingleSubstFormat1
39{
40  friend struct SingleSubst;
41
42  private:
43
44  inline void closure (hb_closure_context_t *c) const
45  {
46    TRACE_CLOSURE ();
47    Coverage::Iter iter;
48    for (iter.init (this+coverage); iter.more (); iter.next ()) {
49      hb_codepoint_t glyph_id = iter.get_glyph ();
50      if (c->glyphs->has (glyph_id))
51	c->glyphs->add ((glyph_id + deltaGlyphID) & 0xFFFF);
52    }
53  }
54
55  inline const Coverage &get_coverage (void) const
56  {
57    return this+coverage;
58  }
59
60  inline bool apply (hb_apply_context_t *c) const
61  {
62    TRACE_APPLY ();
63    hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
64    unsigned int index = (this+coverage) (glyph_id);
65    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
66
67    /* According to the Adobe Annotated OpenType Suite, result is always
68     * limited to 16bit. */
69    glyph_id = (glyph_id + deltaGlyphID) & 0xFFFF;
70    c->replace_glyph (glyph_id);
71
72    return TRACE_RETURN (true);
73  }
74
75  inline bool serialize (hb_serialize_context_t *c,
76			 const USHORT *glyphs,
77			 unsigned int num_glyphs,
78			 unsigned int delta)
79  {
80    TRACE_SERIALIZE ();
81    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
82    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false);
83    deltaGlyphID.set (delta); /* TODO(serilaize) overflow? */
84    return TRACE_RETURN (true);
85  }
86
87  inline bool sanitize (hb_sanitize_context_t *c) {
88    TRACE_SANITIZE ();
89    return TRACE_RETURN (coverage.sanitize (c, this) && deltaGlyphID.sanitize (c));
90  }
91
92  protected:
93  USHORT	format;			/* Format identifier--format = 1 */
94  OffsetTo<Coverage>
95		coverage;		/* Offset to Coverage table--from
96					 * beginning of Substitution table */
97  SHORT		deltaGlyphID;		/* Add to original GlyphID to get
98					 * substitute GlyphID */
99  public:
100  DEFINE_SIZE_STATIC (6);
101};
102
103struct SingleSubstFormat2
104{
105  friend struct SingleSubst;
106
107  private:
108
109  inline void closure (hb_closure_context_t *c) const
110  {
111    TRACE_CLOSURE ();
112    Coverage::Iter iter;
113    for (iter.init (this+coverage); iter.more (); iter.next ()) {
114      if (c->glyphs->has (iter.get_glyph ()))
115	c->glyphs->add (substitute[iter.get_coverage ()]);
116    }
117  }
118
119  inline const Coverage &get_coverage (void) const
120  {
121    return this+coverage;
122  }
123
124  inline bool apply (hb_apply_context_t *c) const
125  {
126    TRACE_APPLY ();
127    hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
128    unsigned int index = (this+coverage) (glyph_id);
129    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
130
131    if (unlikely (index >= substitute.len)) return TRACE_RETURN (false);
132
133    glyph_id = substitute[index];
134    c->replace_glyph (glyph_id);
135
136    return TRACE_RETURN (true);
137  }
138
139  inline bool serialize (hb_serialize_context_t *c,
140			 const USHORT *glyphs,
141			 const USHORT *substitutes,
142			 unsigned int num_glyphs)
143  {
144    TRACE_SERIALIZE ();
145    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
146    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false);
147    if (unlikely (!substitute.serialize (c, substitutes, num_glyphs))) return TRACE_RETURN (false);
148    return TRACE_RETURN (true);
149  }
150
151  inline bool sanitize (hb_sanitize_context_t *c) {
152    TRACE_SANITIZE ();
153    return TRACE_RETURN (coverage.sanitize (c, this) && substitute.sanitize (c));
154  }
155
156  protected:
157  USHORT	format;			/* Format identifier--format = 2 */
158  OffsetTo<Coverage>
159		coverage;		/* Offset to Coverage table--from
160					 * beginning of Substitution table */
161  ArrayOf<GlyphID>
162		substitute;		/* Array of substitute
163					 * GlyphIDs--ordered by Coverage Index */
164  public:
165  DEFINE_SIZE_ARRAY (6, substitute);
166};
167
168struct SingleSubst
169{
170  friend struct SubstLookupSubTable;
171
172  private:
173
174  inline void closure (hb_closure_context_t *c) const
175  {
176    TRACE_CLOSURE ();
177    switch (u.format) {
178    case 1: u.format1.closure (c); break;
179    case 2: u.format2.closure (c); break;
180    default:                       break;
181    }
182  }
183
184  inline const Coverage &get_coverage (void) const
185  {
186    switch (u.format) {
187    case 1: return u.format1.get_coverage ();
188    case 2: return u.format2.get_coverage ();
189    default:return Null(Coverage);
190    }
191  }
192
193  inline bool apply (hb_apply_context_t *c) const
194  {
195    TRACE_APPLY ();
196    switch (u.format) {
197    case 1: return TRACE_RETURN (u.format1.apply (c));
198    case 2: return TRACE_RETURN (u.format2.apply (c));
199    default:return TRACE_RETURN (false);
200    }
201  }
202
203  inline bool serialize (hb_serialize_context_t *c,
204			 const USHORT *glyphs,
205			 const USHORT *substitutes,
206			 unsigned int num_glyphs)
207  {
208    TRACE_SERIALIZE ();
209    if (unlikely (!c->extend_min (u.format))) return TRACE_RETURN (false);
210    unsigned int format = 2;
211    unsigned int delta;
212    if (num_glyphs) {
213      format = 1;
214      /* TODO(serialize) check for wrap-around */
215      delta = substitutes[0] - glyphs[0];
216      for (unsigned int i = 1; i < num_glyphs; i++)
217	if (delta != substitutes[i] - glyphs[i]) {
218	  format = 2;
219	  break;
220	}
221    }
222    u.format.set (format);
223    switch (u.format) {
224    case 1: return TRACE_RETURN (u.format1.serialize (c, glyphs, num_glyphs, delta));
225    case 2: return TRACE_RETURN (u.format2.serialize (c, glyphs, substitutes, num_glyphs));
226    default:return TRACE_RETURN (false);
227    }
228  }
229
230  inline bool sanitize (hb_sanitize_context_t *c) {
231    TRACE_SANITIZE ();
232    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
233    switch (u.format) {
234    case 1: return TRACE_RETURN (u.format1.sanitize (c));
235    case 2: return TRACE_RETURN (u.format2.sanitize (c));
236    default:return TRACE_RETURN (true);
237    }
238  }
239
240  protected:
241  union {
242  USHORT		format;		/* Format identifier */
243  SingleSubstFormat1	format1;
244  SingleSubstFormat2	format2;
245  } u;
246};
247
248
249struct Sequence
250{
251  friend struct MultipleSubstFormat1;
252
253  private:
254
255  inline void closure (hb_closure_context_t *c) const
256  {
257    TRACE_CLOSURE ();
258    unsigned int count = substitute.len;
259    for (unsigned int i = 0; i < count; i++)
260      c->glyphs->add (substitute[i]);
261  }
262
263  inline bool apply (hb_apply_context_t *c) const
264  {
265    TRACE_APPLY ();
266    if (unlikely (!substitute.len)) return TRACE_RETURN (false);
267
268    unsigned int klass = c->property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE ? HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH : 0;
269    unsigned int count = substitute.len;
270    for (unsigned int i = 0; i < count; i++) {
271      set_lig_props_for_component (c->buffer->cur(), i);
272      c->output_glyph (substitute.array[i], klass);
273    }
274    c->buffer->skip_glyph ();
275
276    return TRACE_RETURN (true);
277  }
278
279  inline bool serialize (hb_serialize_context_t *c,
280			 const USHORT *glyphs,
281			 unsigned int num_glyphs)
282  {
283    TRACE_SERIALIZE ();
284    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
285    if (unlikely (!substitute.serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false);
286    return TRACE_RETURN (true);
287  }
288
289  public:
290  inline bool sanitize (hb_sanitize_context_t *c) {
291    TRACE_SANITIZE ();
292    return TRACE_RETURN (substitute.sanitize (c));
293  }
294
295  protected:
296  ArrayOf<GlyphID>
297		substitute;		/* String of GlyphIDs to substitute */
298  public:
299  DEFINE_SIZE_ARRAY (2, substitute);
300};
301
302struct MultipleSubstFormat1
303{
304  friend struct MultipleSubst;
305
306  private:
307
308  inline void closure (hb_closure_context_t *c) const
309  {
310    TRACE_CLOSURE ();
311    Coverage::Iter iter;
312    for (iter.init (this+coverage); iter.more (); iter.next ()) {
313      if (c->glyphs->has (iter.get_glyph ()))
314	(this+sequence[iter.get_coverage ()]).closure (c);
315    }
316  }
317
318  inline const Coverage &get_coverage (void) const
319  {
320    return this+coverage;
321  }
322
323  inline bool apply (hb_apply_context_t *c) const
324  {
325    TRACE_APPLY ();
326
327    unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
328    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
329
330    return TRACE_RETURN ((this+sequence[index]).apply (c));
331  }
332
333  inline bool serialize (hb_serialize_context_t *c,
334			 const USHORT *glyphs,
335			 unsigned int *substitute_len_list,
336			 unsigned int num_glyphs,
337			 const USHORT *substitute_glyphs_list)
338  {
339    TRACE_SERIALIZE ();
340    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
341    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false);
342    if (unlikely (!sequence.serialize (c, num_glyphs))) return TRACE_RETURN (false);
343    for (unsigned int i = 0; i < num_glyphs; i++) {
344      if (unlikely (!sequence[i].serialize (c, this).serialize (c, substitute_glyphs_list, substitute_len_list[i]))) return TRACE_RETURN (false);
345      substitute_glyphs_list += substitute_len_list[i];
346    }
347    return TRACE_RETURN (true);
348  }
349
350  inline bool sanitize (hb_sanitize_context_t *c) {
351    TRACE_SANITIZE ();
352    return TRACE_RETURN (coverage.sanitize (c, this) && sequence.sanitize (c, this));
353  }
354
355  protected:
356  USHORT	format;			/* Format identifier--format = 1 */
357  OffsetTo<Coverage>
358		coverage;		/* Offset to Coverage table--from
359					 * beginning of Substitution table */
360  OffsetArrayOf<Sequence>
361		sequence;		/* Array of Sequence tables
362					 * ordered by Coverage Index */
363  public:
364  DEFINE_SIZE_ARRAY (6, sequence);
365};
366
367struct MultipleSubst
368{
369  friend struct SubstLookupSubTable;
370
371  private:
372
373  inline void closure (hb_closure_context_t *c) const
374  {
375    TRACE_CLOSURE ();
376    switch (u.format) {
377    case 1: u.format1.closure (c); break;
378    default:                       break;
379    }
380  }
381
382  inline const Coverage &get_coverage (void) const
383  {
384    switch (u.format) {
385    case 1: return u.format1.get_coverage ();
386    default:return Null(Coverage);
387    }
388  }
389
390  inline bool apply (hb_apply_context_t *c) const
391  {
392    TRACE_APPLY ();
393    switch (u.format) {
394    case 1: return TRACE_RETURN (u.format1.apply (c));
395    default:return TRACE_RETURN (false);
396    }
397  }
398
399  inline bool serialize (hb_serialize_context_t *c,
400			 const USHORT *glyphs,
401			 unsigned int *substitute_len_list,
402			 unsigned int num_glyphs,
403			 const USHORT *substitute_glyphs_list)
404  {
405    TRACE_SERIALIZE ();
406    if (unlikely (!c->extend_min (u.format))) return TRACE_RETURN (false);
407    unsigned int format = 1;
408    u.format.set (format);
409    switch (u.format) {
410    case 1: return TRACE_RETURN (u.format1.serialize (c, glyphs, substitute_len_list, num_glyphs, substitute_glyphs_list));
411    default:return TRACE_RETURN (false);
412    }
413  }
414
415  inline bool sanitize (hb_sanitize_context_t *c) {
416    TRACE_SANITIZE ();
417    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
418    switch (u.format) {
419    case 1: return TRACE_RETURN (u.format1.sanitize (c));
420    default:return TRACE_RETURN (true);
421    }
422  }
423
424  protected:
425  union {
426  USHORT		format;		/* Format identifier */
427  MultipleSubstFormat1	format1;
428  } u;
429};
430
431
432typedef ArrayOf<GlyphID> AlternateSet;	/* Array of alternate GlyphIDs--in
433					 * arbitrary order */
434
435struct AlternateSubstFormat1
436{
437  friend struct AlternateSubst;
438
439  private:
440
441  inline void closure (hb_closure_context_t *c) const
442  {
443    TRACE_CLOSURE ();
444    Coverage::Iter iter;
445    for (iter.init (this+coverage); iter.more (); iter.next ()) {
446      if (c->glyphs->has (iter.get_glyph ())) {
447	const AlternateSet &alt_set = this+alternateSet[iter.get_coverage ()];
448	unsigned int count = alt_set.len;
449	for (unsigned int i = 0; i < count; i++)
450	  c->glyphs->add (alt_set[i]);
451      }
452    }
453  }
454
455  inline const Coverage &get_coverage (void) const
456  {
457    return this+coverage;
458  }
459
460  inline bool apply (hb_apply_context_t *c) const
461  {
462    TRACE_APPLY ();
463    hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
464
465    unsigned int index = (this+coverage) (glyph_id);
466    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
467
468    const AlternateSet &alt_set = this+alternateSet[index];
469
470    if (unlikely (!alt_set.len)) return TRACE_RETURN (false);
471
472    hb_mask_t glyph_mask = c->buffer->cur().mask;
473    hb_mask_t lookup_mask = c->lookup_mask;
474
475    /* Note: This breaks badly if two features enabled this lookup together. */
476    unsigned int shift = _hb_ctz (lookup_mask);
477    unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift);
478
479    if (unlikely (alt_index > alt_set.len || alt_index == 0)) return TRACE_RETURN (false);
480
481    glyph_id = alt_set[alt_index - 1];
482
483    c->replace_glyph (glyph_id);
484
485    return TRACE_RETURN (true);
486  }
487
488  inline bool serialize (hb_serialize_context_t *c,
489			 const USHORT *glyphs,
490			 unsigned int *alternate_len_list,
491			 unsigned int num_glyphs,
492			 const USHORT *alternate_glyphs_list)
493  {
494    TRACE_SERIALIZE ();
495    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
496    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false);
497    if (unlikely (!alternateSet.serialize (c, num_glyphs))) return TRACE_RETURN (false);
498    for (unsigned int i = 0; i < num_glyphs; i++) {
499      if (unlikely (!alternateSet[i].serialize (c, this).serialize (c, alternate_glyphs_list, alternate_len_list[i]))) return TRACE_RETURN (false);
500      alternate_glyphs_list += alternate_len_list[i];
501    }
502    return TRACE_RETURN (true);
503  }
504
505  inline bool sanitize (hb_sanitize_context_t *c) {
506    TRACE_SANITIZE ();
507    return TRACE_RETURN (coverage.sanitize (c, this) && alternateSet.sanitize (c, this));
508  }
509
510  protected:
511  USHORT	format;			/* Format identifier--format = 1 */
512  OffsetTo<Coverage>
513		coverage;		/* Offset to Coverage table--from
514					 * beginning of Substitution table */
515  OffsetArrayOf<AlternateSet>
516		alternateSet;		/* Array of AlternateSet tables
517					 * ordered by Coverage Index */
518  public:
519  DEFINE_SIZE_ARRAY (6, alternateSet);
520};
521
522struct AlternateSubst
523{
524  friend struct SubstLookupSubTable;
525
526  private:
527
528  inline void closure (hb_closure_context_t *c) const
529  {
530    TRACE_CLOSURE ();
531    switch (u.format) {
532    case 1: u.format1.closure (c); break;
533    default:                       break;
534    }
535  }
536
537  inline const Coverage &get_coverage (void) const
538  {
539    switch (u.format) {
540    case 1: return u.format1.get_coverage ();
541    default:return Null(Coverage);
542    }
543  }
544
545  inline bool apply (hb_apply_context_t *c) const
546  {
547    TRACE_APPLY ();
548    switch (u.format) {
549    case 1: return TRACE_RETURN (u.format1.apply (c));
550    default:return TRACE_RETURN (false);
551    }
552  }
553
554  inline bool serialize (hb_serialize_context_t *c,
555			 const USHORT *glyphs,
556			 unsigned int *alternate_len_list,
557			 unsigned int num_glyphs,
558			 const USHORT *alternate_glyphs_list)
559  {
560    TRACE_SERIALIZE ();
561    if (unlikely (!c->extend_min (u.format))) return TRACE_RETURN (false);
562    unsigned int format = 1;
563    u.format.set (format);
564    switch (u.format) {
565    case 1: return TRACE_RETURN (u.format1.serialize (c, glyphs, alternate_len_list, num_glyphs, alternate_glyphs_list));
566    default:return TRACE_RETURN (false);
567    }
568  }
569
570  inline bool sanitize (hb_sanitize_context_t *c) {
571    TRACE_SANITIZE ();
572    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
573    switch (u.format) {
574    case 1: return TRACE_RETURN (u.format1.sanitize (c));
575    default:return TRACE_RETURN (true);
576    }
577  }
578
579  protected:
580  union {
581  USHORT		format;		/* Format identifier */
582  AlternateSubstFormat1	format1;
583  } u;
584};
585
586
587struct Ligature
588{
589  friend struct LigatureSet;
590
591  private:
592
593  inline void closure (hb_closure_context_t *c) const
594  {
595    TRACE_CLOSURE ();
596    unsigned int count = component.len;
597    for (unsigned int i = 1; i < count; i++)
598      if (!c->glyphs->has (component[i]))
599        return;
600    c->glyphs->add (ligGlyph);
601  }
602
603  inline bool would_apply (hb_would_apply_context_t *c) const
604  {
605    if (c->len != component.len)
606      return false;
607
608    for (unsigned int i = 1; i < c->len; i++)
609      if (likely (c->glyphs[i] != component[i]))
610	return false;
611
612    return true;
613  }
614
615  inline bool apply (hb_apply_context_t *c) const
616  {
617    TRACE_APPLY ();
618    unsigned int count = component.len;
619    if (unlikely (count < 1)) return TRACE_RETURN (false);
620
621    unsigned int end_offset;
622    bool is_mark_ligature;
623    unsigned int total_component_count;
624
625    if (likely (!match_input (c, count,
626			      &component[1],
627			      match_glyph,
628			      NULL,
629			      &end_offset,
630			      &is_mark_ligature,
631			      &total_component_count)))
632      return TRACE_RETURN (false);
633
634    /* Deal, we are forming the ligature. */
635    c->buffer->merge_clusters (c->buffer->idx, c->buffer->idx + end_offset);
636
637    ligate_input (c,
638		  count,
639		  &component[1],
640		  ligGlyph,
641		  match_glyph,
642		  NULL,
643		  is_mark_ligature,
644		  total_component_count);
645
646    return TRACE_RETURN (true);
647  }
648
649  public:
650  inline bool sanitize (hb_sanitize_context_t *c) {
651    TRACE_SANITIZE ();
652    return TRACE_RETURN (ligGlyph.sanitize (c) && component.sanitize (c));
653  }
654
655  protected:
656  GlyphID	ligGlyph;		/* GlyphID of ligature to substitute */
657  HeadlessArrayOf<GlyphID>
658		component;		/* Array of component GlyphIDs--start
659					 * with the second  component--ordered
660					 * in writing direction */
661  public:
662  DEFINE_SIZE_ARRAY (4, component);
663};
664
665struct LigatureSet
666{
667  friend struct LigatureSubstFormat1;
668
669  private:
670
671  inline void closure (hb_closure_context_t *c) const
672  {
673    TRACE_CLOSURE ();
674    unsigned int num_ligs = ligature.len;
675    for (unsigned int i = 0; i < num_ligs; i++)
676      (this+ligature[i]).closure (c);
677  }
678
679  inline bool would_apply (hb_would_apply_context_t *c) const
680  {
681    unsigned int num_ligs = ligature.len;
682    for (unsigned int i = 0; i < num_ligs; i++)
683    {
684      const Ligature &lig = this+ligature[i];
685      if (lig.would_apply (c))
686        return true;
687    }
688    return false;
689  }
690
691  inline bool apply (hb_apply_context_t *c) const
692  {
693    TRACE_APPLY ();
694    unsigned int num_ligs = ligature.len;
695    for (unsigned int i = 0; i < num_ligs; i++)
696    {
697      const Ligature &lig = this+ligature[i];
698      if (lig.apply (c)) return TRACE_RETURN (true);
699    }
700
701    return TRACE_RETURN (false);
702  }
703
704  public:
705  inline bool sanitize (hb_sanitize_context_t *c) {
706    TRACE_SANITIZE ();
707    return TRACE_RETURN (ligature.sanitize (c, this));
708  }
709
710  protected:
711  OffsetArrayOf<Ligature>
712		ligature;		/* Array LigatureSet tables
713					 * ordered by preference */
714  public:
715  DEFINE_SIZE_ARRAY (2, ligature);
716};
717
718struct LigatureSubstFormat1
719{
720  friend struct LigatureSubst;
721
722  private:
723
724  inline void closure (hb_closure_context_t *c) const
725  {
726    TRACE_CLOSURE ();
727    Coverage::Iter iter;
728    for (iter.init (this+coverage); iter.more (); iter.next ()) {
729      if (c->glyphs->has (iter.get_glyph ()))
730	(this+ligatureSet[iter.get_coverage ()]).closure (c);
731    }
732  }
733
734  inline const Coverage &get_coverage (void) const
735  {
736    return this+coverage;
737  }
738
739  inline bool would_apply (hb_would_apply_context_t *c) const
740  {
741    return (this+ligatureSet[(this+coverage) (c->glyphs[0])]).would_apply (c);
742  }
743
744  inline bool apply (hb_apply_context_t *c) const
745  {
746    TRACE_APPLY ();
747    hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
748
749    unsigned int index = (this+coverage) (glyph_id);
750    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
751
752    const LigatureSet &lig_set = this+ligatureSet[index];
753    return TRACE_RETURN (lig_set.apply (c));
754  }
755
756  inline bool sanitize (hb_sanitize_context_t *c) {
757    TRACE_SANITIZE ();
758    return TRACE_RETURN (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this));
759  }
760
761  protected:
762  USHORT	format;			/* Format identifier--format = 1 */
763  OffsetTo<Coverage>
764		coverage;		/* Offset to Coverage table--from
765					 * beginning of Substitution table */
766  OffsetArrayOf<LigatureSet>
767		ligatureSet;		/* Array LigatureSet tables
768					 * ordered by Coverage Index */
769  public:
770  DEFINE_SIZE_ARRAY (6, ligatureSet);
771};
772
773struct LigatureSubst
774{
775  friend struct SubstLookupSubTable;
776
777  private:
778
779  inline void closure (hb_closure_context_t *c) const
780  {
781    TRACE_CLOSURE ();
782    switch (u.format) {
783    case 1: u.format1.closure (c); break;
784    default:                       break;
785    }
786  }
787
788  inline const Coverage &get_coverage (void) const
789  {
790    switch (u.format) {
791    case 1: return u.format1.get_coverage ();
792    default:return Null(Coverage);
793    }
794  }
795
796  inline bool would_apply (hb_would_apply_context_t *c) const
797  {
798    switch (u.format) {
799    case 1: return u.format1.would_apply (c);
800    default:return false;
801    }
802  }
803
804  inline bool apply (hb_apply_context_t *c) const
805  {
806    TRACE_APPLY ();
807    switch (u.format) {
808    case 1: return TRACE_RETURN (u.format1.apply (c));
809    default:return TRACE_RETURN (false);
810    }
811  }
812
813  inline bool sanitize (hb_sanitize_context_t *c) {
814    TRACE_SANITIZE ();
815    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
816    switch (u.format) {
817    case 1: return TRACE_RETURN (u.format1.sanitize (c));
818    default:return TRACE_RETURN (true);
819    }
820  }
821
822  protected:
823  union {
824  USHORT		format;		/* Format identifier */
825  LigatureSubstFormat1	format1;
826  } u;
827};
828
829
830static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index);
831static inline void closure_lookup (hb_closure_context_t *c, unsigned int lookup_index);
832
833struct ContextSubst : Context
834{
835  friend struct SubstLookupSubTable;
836
837  private:
838
839  inline void closure (hb_closure_context_t *c) const
840  {
841    TRACE_CLOSURE ();
842    return Context::closure (c, closure_lookup);
843  }
844
845  inline bool apply (hb_apply_context_t *c) const
846  {
847    TRACE_APPLY ();
848    return TRACE_RETURN (Context::apply (c, substitute_lookup));
849  }
850};
851
852struct ChainContextSubst : ChainContext
853{
854  friend struct SubstLookupSubTable;
855
856  private:
857
858  inline void closure (hb_closure_context_t *c) const
859  {
860    TRACE_CLOSURE ();
861    return ChainContext::closure (c, closure_lookup);
862  }
863
864  inline bool apply (hb_apply_context_t *c) const
865  {
866    TRACE_APPLY ();
867    return TRACE_RETURN (ChainContext::apply (c, substitute_lookup));
868  }
869};
870
871
872struct ExtensionSubst : Extension
873{
874  friend struct SubstLookupSubTable;
875  friend struct SubstLookup;
876
877  private:
878  inline const struct SubstLookupSubTable& get_subtable (void) const
879  {
880    unsigned int offset = get_offset ();
881    if (unlikely (!offset)) return Null(SubstLookupSubTable);
882    return StructAtOffset<SubstLookupSubTable> (this, offset);
883  }
884
885  inline void closure (hb_closure_context_t *c) const;
886
887  inline const Coverage &get_coverage (void) const;
888
889  inline bool would_apply (hb_would_apply_context_t *c) const;
890
891  inline bool apply (hb_apply_context_t *c) const;
892
893  inline bool sanitize (hb_sanitize_context_t *c);
894
895  inline bool is_reverse (void) const;
896};
897
898
899struct ReverseChainSingleSubstFormat1
900{
901  friend struct ReverseChainSingleSubst;
902
903  private:
904
905  inline void closure (hb_closure_context_t *c) const
906  {
907    TRACE_CLOSURE ();
908    const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
909
910    unsigned int count;
911
912    count = backtrack.len;
913    for (unsigned int i = 0; i < count; i++)
914      if (!(this+backtrack[i]).intersects (c->glyphs))
915        return;
916
917    count = lookahead.len;
918    for (unsigned int i = 0; i < count; i++)
919      if (!(this+lookahead[i]).intersects (c->glyphs))
920        return;
921
922    const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
923    Coverage::Iter iter;
924    for (iter.init (this+coverage); iter.more (); iter.next ()) {
925      if (c->glyphs->has (iter.get_glyph ()))
926	c->glyphs->add (substitute[iter.get_coverage ()]);
927    }
928  }
929
930  inline const Coverage &get_coverage (void) const
931  {
932    return this+coverage;
933  }
934
935  inline bool apply (hb_apply_context_t *c) const
936  {
937    TRACE_APPLY ();
938    if (unlikely (c->nesting_level_left != MAX_NESTING_LEVEL))
939      return TRACE_RETURN (false); /* No chaining to this type */
940
941    unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
942    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
943
944    const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
945    const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
946
947    if (match_backtrack (c,
948			 backtrack.len, (USHORT *) backtrack.array,
949			 match_coverage, this) &&
950        match_lookahead (c,
951			 lookahead.len, (USHORT *) lookahead.array,
952			 match_coverage, this,
953			 1))
954    {
955      c->replace_glyph_inplace (substitute[index]);
956      c->buffer->idx--; /* Reverse! */
957      return TRACE_RETURN (true);
958    }
959
960    return TRACE_RETURN (false);
961  }
962
963  inline bool sanitize (hb_sanitize_context_t *c) {
964    TRACE_SANITIZE ();
965    if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this)))
966      return TRACE_RETURN (false);
967    OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
968    if (!lookahead.sanitize (c, this))
969      return TRACE_RETURN (false);
970    ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
971    return TRACE_RETURN (substitute.sanitize (c));
972  }
973
974  protected:
975  USHORT	format;			/* Format identifier--format = 1 */
976  OffsetTo<Coverage>
977		coverage;		/* Offset to Coverage table--from
978					 * beginning of table */
979  OffsetArrayOf<Coverage>
980		backtrack;		/* Array of coverage tables
981					 * in backtracking sequence, in  glyph
982					 * sequence order */
983  OffsetArrayOf<Coverage>
984		lookaheadX;		/* Array of coverage tables
985					 * in lookahead sequence, in glyph
986					 * sequence order */
987  ArrayOf<GlyphID>
988		substituteX;		/* Array of substitute
989					 * GlyphIDs--ordered by Coverage Index */
990  public:
991  DEFINE_SIZE_MIN (10);
992};
993
994struct ReverseChainSingleSubst
995{
996  friend struct SubstLookupSubTable;
997
998  private:
999
1000  inline void closure (hb_closure_context_t *c) const
1001  {
1002    TRACE_CLOSURE ();
1003    switch (u.format) {
1004    case 1: u.format1.closure (c); break;
1005    default:                       break;
1006    }
1007  }
1008
1009  inline const Coverage &get_coverage (void) const
1010  {
1011    switch (u.format) {
1012    case 1: return u.format1.get_coverage ();
1013    default:return Null(Coverage);
1014    }
1015  }
1016
1017  inline bool apply (hb_apply_context_t *c) const
1018  {
1019    TRACE_APPLY ();
1020    switch (u.format) {
1021    case 1: return TRACE_RETURN (u.format1.apply (c));
1022    default:return TRACE_RETURN (false);
1023    }
1024  }
1025
1026  inline bool sanitize (hb_sanitize_context_t *c) {
1027    TRACE_SANITIZE ();
1028    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
1029    switch (u.format) {
1030    case 1: return TRACE_RETURN (u.format1.sanitize (c));
1031    default:return TRACE_RETURN (true);
1032    }
1033  }
1034
1035  protected:
1036  union {
1037  USHORT				format;		/* Format identifier */
1038  ReverseChainSingleSubstFormat1	format1;
1039  } u;
1040};
1041
1042
1043
1044/*
1045 * SubstLookup
1046 */
1047
1048struct SubstLookupSubTable
1049{
1050  friend struct SubstLookup;
1051
1052  enum Type {
1053    Single		= 1,
1054    Multiple		= 2,
1055    Alternate		= 3,
1056    Ligature		= 4,
1057    Context		= 5,
1058    ChainContext	= 6,
1059    Extension		= 7,
1060    ReverseChainSingle	= 8
1061  };
1062
1063  inline void closure (hb_closure_context_t *c,
1064		       unsigned int    lookup_type) const
1065  {
1066    TRACE_CLOSURE ();
1067    switch (lookup_type) {
1068    case Single:		u.single.closure (c); break;
1069    case Multiple:		u.multiple.closure (c); break;
1070    case Alternate:		u.alternate.closure (c); break;
1071    case Ligature:		u.ligature.closure (c); break;
1072    case Context:		u.context.closure (c); break;
1073    case ChainContext:		u.chainContext.closure (c); break;
1074    case Extension:		u.extension.closure (c); break;
1075    case ReverseChainSingle:	u.reverseChainContextSingle.closure (c); break;
1076    default:                    break;
1077    }
1078  }
1079
1080  inline const Coverage &get_coverage (unsigned int lookup_type) const
1081  {
1082    switch (lookup_type) {
1083    case Single:		return u.single.get_coverage ();
1084    case Multiple:		return u.multiple.get_coverage ();
1085    case Alternate:		return u.alternate.get_coverage ();
1086    case Ligature:		return u.ligature.get_coverage ();
1087    case Context:		return u.context.get_coverage ();
1088    case ChainContext:		return u.chainContext.get_coverage ();
1089    case Extension:		return u.extension.get_coverage ();
1090    case ReverseChainSingle:	return u.reverseChainContextSingle.get_coverage ();
1091    default:			return Null(Coverage);
1092    }
1093  }
1094
1095  inline bool would_apply (hb_would_apply_context_t *c,
1096			   unsigned int lookup_type) const
1097  {
1098    TRACE_WOULD_APPLY ();
1099    if (get_coverage (lookup_type).get_coverage (c->glyphs[0]) == NOT_COVERED) return false;
1100    if (c->len == 1) {
1101      switch (lookup_type) {
1102      case Single:
1103      case Multiple:
1104      case Alternate:
1105      case ReverseChainSingle:
1106        return true;
1107      }
1108    }
1109
1110    /* Only need to look further for lookups that support substitutions
1111     * of input longer than 1. */
1112    switch (lookup_type) {
1113    case Ligature:		return u.ligature.would_apply (c);
1114    case Context:		return u.context.would_apply (c);
1115    case ChainContext:		return u.chainContext.would_apply (c);
1116    case Extension:		return u.extension.would_apply (c);
1117    default:			return false;
1118    }
1119  }
1120
1121  inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const
1122  {
1123    TRACE_APPLY ();
1124    switch (lookup_type) {
1125    case Single:		return TRACE_RETURN (u.single.apply (c));
1126    case Multiple:		return TRACE_RETURN (u.multiple.apply (c));
1127    case Alternate:		return TRACE_RETURN (u.alternate.apply (c));
1128    case Ligature:		return TRACE_RETURN (u.ligature.apply (c));
1129    case Context:		return TRACE_RETURN (u.context.apply (c));
1130    case ChainContext:		return TRACE_RETURN (u.chainContext.apply (c));
1131    case Extension:		return TRACE_RETURN (u.extension.apply (c));
1132    case ReverseChainSingle:	return TRACE_RETURN (u.reverseChainContextSingle.apply (c));
1133    default:			return TRACE_RETURN (false);
1134    }
1135  }
1136
1137  inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) {
1138    TRACE_SANITIZE ();
1139    if (!u.header.sub_format.sanitize (c))
1140      return TRACE_RETURN (false);
1141    switch (lookup_type) {
1142    case Single:		return TRACE_RETURN (u.single.sanitize (c));
1143    case Multiple:		return TRACE_RETURN (u.multiple.sanitize (c));
1144    case Alternate:		return TRACE_RETURN (u.alternate.sanitize (c));
1145    case Ligature:		return TRACE_RETURN (u.ligature.sanitize (c));
1146    case Context:		return TRACE_RETURN (u.context.sanitize (c));
1147    case ChainContext:		return TRACE_RETURN (u.chainContext.sanitize (c));
1148    case Extension:		return TRACE_RETURN (u.extension.sanitize (c));
1149    case ReverseChainSingle:	return TRACE_RETURN (u.reverseChainContextSingle.sanitize (c));
1150    default:			return TRACE_RETURN (true);
1151    }
1152  }
1153
1154  protected:
1155  union {
1156  struct {
1157    USHORT			sub_format;
1158  } header;
1159  SingleSubst			single;
1160  MultipleSubst			multiple;
1161  AlternateSubst		alternate;
1162  LigatureSubst			ligature;
1163  ContextSubst			context;
1164  ChainContextSubst		chainContext;
1165  ExtensionSubst		extension;
1166  ReverseChainSingleSubst	reverseChainContextSingle;
1167  } u;
1168  public:
1169  DEFINE_SIZE_UNION (2, header.sub_format);
1170};
1171
1172
1173struct SubstLookup : Lookup
1174{
1175  inline const SubstLookupSubTable& get_subtable (unsigned int i) const
1176  { return this+CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable)[i]; }
1177
1178  inline static bool lookup_type_is_reverse (unsigned int lookup_type)
1179  { return lookup_type == SubstLookupSubTable::ReverseChainSingle; }
1180
1181  inline bool is_reverse (void) const
1182  {
1183    unsigned int type = get_type ();
1184    if (unlikely (type == SubstLookupSubTable::Extension))
1185      return CastR<ExtensionSubst> (get_subtable(0)).is_reverse ();
1186    return lookup_type_is_reverse (type);
1187  }
1188
1189  inline void closure (hb_closure_context_t *c) const
1190  {
1191    unsigned int lookup_type = get_type ();
1192    unsigned int count = get_subtable_count ();
1193    for (unsigned int i = 0; i < count; i++)
1194      get_subtable (i).closure (c, lookup_type);
1195  }
1196
1197  template <typename set_t>
1198  inline void add_coverage (set_t *glyphs) const
1199  {
1200    const Coverage *last = NULL;
1201    unsigned int count = get_subtable_count ();
1202    for (unsigned int i = 0; i < count; i++) {
1203      const Coverage *c = &get_subtable (i).get_coverage (get_type ());
1204      if (c != last) {
1205        c->add_coverage (glyphs);
1206        last = c;
1207      }
1208    }
1209  }
1210
1211  inline bool would_apply (hb_would_apply_context_t *c) const
1212  {
1213    if (unlikely (!c->len)) return false;
1214    if (!c->digest.may_have (c->glyphs[0])) return false;
1215    unsigned int lookup_type = get_type ();
1216    unsigned int count = get_subtable_count ();
1217    for (unsigned int i = 0; i < count; i++)
1218      if (get_subtable (i).would_apply (c, lookup_type))
1219	return true;
1220    return false;
1221  }
1222
1223  inline bool apply_once (hb_apply_context_t *c) const
1224  {
1225    unsigned int lookup_type = get_type ();
1226
1227    if (!c->check_glyph_property (&c->buffer->cur(), c->lookup_props, &c->property))
1228      return false;
1229
1230    unsigned int count = get_subtable_count ();
1231    for (unsigned int i = 0; i < count; i++)
1232      if (get_subtable (i).apply (c, lookup_type))
1233	return true;
1234
1235    return false;
1236  }
1237
1238  inline bool apply_string (hb_apply_context_t *c) const
1239  {
1240    bool ret = false;
1241
1242    if (unlikely (!c->buffer->len))
1243      return false;
1244
1245    c->set_lookup (*this);
1246
1247    if (likely (!is_reverse ()))
1248    {
1249	/* in/out forward substitution */
1250	c->buffer->clear_output ();
1251	c->buffer->idx = 0;
1252
1253	while (c->buffer->idx < c->buffer->len)
1254	{
1255	  if ((c->buffer->cur().mask & c->lookup_mask) &&
1256	      c->digest.may_have (c->buffer->cur().codepoint) &&
1257	      apply_once (c))
1258	    ret = true;
1259	  else
1260	    c->buffer->next_glyph ();
1261	}
1262	if (ret)
1263	  c->buffer->swap_buffers ();
1264    }
1265    else
1266    {
1267	/* in-place backward substitution */
1268	c->buffer->idx = c->buffer->len - 1;
1269	do
1270	{
1271	  if ((c->buffer->cur().mask & c->lookup_mask) &&
1272	      c->digest.may_have (c->buffer->cur().codepoint) &&
1273	      apply_once (c))
1274	    ret = true;
1275	  else
1276	    c->buffer->idx--;
1277
1278	}
1279	while ((int) c->buffer->idx >= 0);
1280    }
1281
1282    return ret;
1283  }
1284
1285  inline bool sanitize (hb_sanitize_context_t *c) {
1286    TRACE_SANITIZE ();
1287    if (unlikely (!Lookup::sanitize (c))) return TRACE_RETURN (false);
1288    OffsetArrayOf<SubstLookupSubTable> &list = CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable);
1289    if (unlikely (!list.sanitize (c, this, get_type ()))) return TRACE_RETURN (false);
1290
1291    if (unlikely (get_type () == SubstLookupSubTable::Extension))
1292    {
1293      /* The spec says all subtables of an Extension lookup should
1294       * have the same type.  This is specially important if one has
1295       * a reverse type!
1296       *
1297       * We just check that they are all either forward, or reverse. */
1298      unsigned int type = get_subtable (0).u.extension.get_type ();
1299      unsigned int count = get_subtable_count ();
1300      for (unsigned int i = 1; i < count; i++)
1301        if (get_subtable (i).u.extension.get_type () != type)
1302	  return TRACE_RETURN (false);
1303    }
1304    return TRACE_RETURN (true);
1305  }
1306};
1307
1308typedef OffsetListOf<SubstLookup> SubstLookupList;
1309
1310/*
1311 * GSUB -- The Glyph Substitution Table
1312 */
1313
1314struct GSUB : GSUBGPOS
1315{
1316  static const hb_tag_t Tag	= HB_OT_TAG_GSUB;
1317
1318  inline const SubstLookup& get_lookup (unsigned int i) const
1319  { return CastR<SubstLookup> (GSUBGPOS::get_lookup (i)); }
1320
1321  template <typename set_t>
1322  inline void add_coverage (set_t *glyphs, unsigned int lookup_index) const
1323  { get_lookup (lookup_index).add_coverage (glyphs); }
1324
1325  inline bool would_substitute_lookup (hb_would_apply_context_t *c, unsigned int lookup_index) const
1326  { return get_lookup (lookup_index).would_apply (c); }
1327
1328  inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index) const
1329  { return get_lookup (lookup_index).apply_string (c); }
1330
1331  static inline void substitute_start (hb_font_t *font, hb_buffer_t *buffer);
1332  static inline void substitute_finish (hb_font_t *font, hb_buffer_t *buffer);
1333
1334  inline void closure_lookup (hb_closure_context_t *c,
1335			      unsigned int          lookup_index) const
1336  { return get_lookup (lookup_index).closure (c); }
1337
1338  inline bool sanitize (hb_sanitize_context_t *c) {
1339    TRACE_SANITIZE ();
1340    if (unlikely (!GSUBGPOS::sanitize (c))) return TRACE_RETURN (false);
1341    OffsetTo<SubstLookupList> &list = CastR<OffsetTo<SubstLookupList> > (lookupList);
1342    return TRACE_RETURN (list.sanitize (c, this));
1343  }
1344  public:
1345  DEFINE_SIZE_STATIC (10);
1346};
1347
1348
1349void
1350GSUB::substitute_start (hb_font_t *font, hb_buffer_t *buffer)
1351{
1352  HB_BUFFER_ALLOCATE_VAR (buffer, glyph_props);
1353  HB_BUFFER_ALLOCATE_VAR (buffer, lig_props);
1354  HB_BUFFER_ALLOCATE_VAR (buffer, syllable);
1355
1356  const GDEF &gdef = *hb_ot_layout_from_face (font->face)->gdef;
1357  unsigned int count = buffer->len;
1358  for (unsigned int i = 0; i < count; i++) {
1359    buffer->info[i].lig_props() = buffer->info[i].syllable() = 0;
1360    buffer->info[i].glyph_props() = gdef.get_glyph_props (buffer->info[i].codepoint);
1361  }
1362}
1363
1364void
1365GSUB::substitute_finish (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer HB_UNUSED)
1366{
1367}
1368
1369
1370/* Out-of-class implementation for methods recursing */
1371
1372inline void ExtensionSubst::closure (hb_closure_context_t *c) const
1373{
1374  get_subtable ().closure (c, get_type ());
1375}
1376
1377inline const Coverage & ExtensionSubst::get_coverage (void) const
1378{
1379  return get_subtable ().get_coverage (get_type ());
1380}
1381
1382inline bool ExtensionSubst::would_apply (hb_would_apply_context_t *c) const
1383{
1384  return get_subtable ().would_apply (c, get_type ());
1385}
1386
1387inline bool ExtensionSubst::apply (hb_apply_context_t *c) const
1388{
1389  TRACE_APPLY ();
1390  return TRACE_RETURN (get_subtable ().apply (c, get_type ()));
1391}
1392
1393inline bool ExtensionSubst::sanitize (hb_sanitize_context_t *c)
1394{
1395  TRACE_SANITIZE ();
1396  if (unlikely (!Extension::sanitize (c))) return TRACE_RETURN (false);
1397  unsigned int offset = get_offset ();
1398  if (unlikely (!offset)) return TRACE_RETURN (true);
1399  return TRACE_RETURN (StructAtOffset<SubstLookupSubTable> (this, offset).sanitize (c, get_type ()));
1400}
1401
1402inline bool ExtensionSubst::is_reverse (void) const
1403{
1404  unsigned int type = get_type ();
1405  if (unlikely (type == SubstLookupSubTable::Extension))
1406    return CastR<ExtensionSubst> (get_subtable()).is_reverse ();
1407  return SubstLookup::lookup_type_is_reverse (type);
1408}
1409
1410static inline void closure_lookup (hb_closure_context_t *c, unsigned int lookup_index)
1411{
1412  const GSUB &gsub = *(hb_ot_layout_from_face (c->face)->gsub);
1413  const SubstLookup &l = gsub.get_lookup (lookup_index);
1414
1415  if (unlikely (c->nesting_level_left == 0))
1416    return;
1417
1418  c->nesting_level_left--;
1419  l.closure (c);
1420  c->nesting_level_left++;
1421}
1422
1423static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index)
1424{
1425  const GSUB &gsub = *(hb_ot_layout_from_face (c->face)->gsub);
1426  const SubstLookup &l = gsub.get_lookup (lookup_index);
1427
1428  if (unlikely (c->nesting_level_left == 0))
1429    return false;
1430
1431  hb_apply_context_t new_c (*c);
1432  new_c.nesting_level_left--;
1433  new_c.set_lookup (l);
1434  return l.apply_once (&new_c);
1435}
1436
1437
1438} // namespace OT
1439
1440
1441#endif /* HB_OT_LAYOUT_GSUB_TABLE_HH */
1442