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