hb-ot-layout-gsubgpos-private.hh revision cee7187447b76b22e1bb6136d137b35ac49c3a5d
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_GSUBGPOS_PRIVATE_HH
30#define HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH
31
32#include "hb-buffer-private.hh"
33#include "hb-ot-layout-gdef-table.hh"
34
35
36
37/* unique ligature id */
38/* component number in the ligature (0 = base) */
39static inline void
40set_lig_props (hb_glyph_info_t &info, unsigned int lig_id, unsigned int lig_comp)
41{
42  info.lig_props() = (lig_id << 4) | (lig_comp & 0x0F);
43}
44static inline unsigned int
45get_lig_id (hb_glyph_info_t &info)
46{
47  return info.lig_props() >> 4;
48}
49static inline unsigned int
50get_lig_comp (hb_glyph_info_t &info)
51{
52  return info.lig_props() & 0x0F;
53}
54
55static inline uint8_t allocate_lig_id (hb_buffer_t *buffer) {
56  uint8_t lig_id = buffer->next_serial () & 0x0F;
57  if (unlikely (!lig_id))
58    lig_id = allocate_lig_id (buffer); /* in case of overflow */
59  return lig_id;
60}
61
62
63
64#ifndef HB_DEBUG_CLOSURE
65#define HB_DEBUG_CLOSURE (HB_DEBUG+0)
66#endif
67
68#define TRACE_CLOSURE() \
69	hb_auto_trace_t<HB_DEBUG_CLOSURE> trace (&c->debug_depth, "CLOSURE", this, HB_FUNC, "");
70
71
72/* TODO Add TRACE_RETURN annotation for would_apply */
73
74
75struct hb_closure_context_t
76{
77  hb_face_t *face;
78  hb_set_t *glyphs;
79  unsigned int nesting_level_left;
80  unsigned int debug_depth;
81
82
83  hb_closure_context_t (hb_face_t *face_,
84			hb_set_t *glyphs_,
85		        unsigned int nesting_level_left_ = MAX_NESTING_LEVEL) :
86			  face (face_), glyphs (glyphs_),
87			  nesting_level_left (nesting_level_left_),
88			  debug_depth (0) {}
89};
90
91
92
93#ifndef HB_DEBUG_APPLY
94#define HB_DEBUG_APPLY (HB_DEBUG+0)
95#endif
96
97#define TRACE_APPLY() \
98	hb_auto_trace_t<HB_DEBUG_APPLY> trace (&c->debug_depth, "APPLY", this, HB_FUNC, "idx %d codepoint %u", c->buffer->idx, c->buffer->info[c->buffer->idx].codepoint);
99
100
101
102struct hb_apply_context_t
103{
104  hb_font_t *font;
105  hb_face_t *face;
106  hb_buffer_t *buffer;
107  hb_direction_t direction;
108  hb_mask_t lookup_mask;
109  unsigned int context_length;
110  unsigned int nesting_level_left;
111  unsigned int lookup_props;
112  unsigned int property; /* propety of first glyph */
113  unsigned int debug_depth;
114
115
116  hb_apply_context_t (hb_font_t *font_,
117		      hb_face_t *face_,
118		      hb_buffer_t *buffer_,
119		      hb_mask_t lookup_mask_,
120		      unsigned int context_length_ = NO_CONTEXT,
121		      unsigned int nesting_level_left_ = MAX_NESTING_LEVEL) :
122			font (font_), face (face_), buffer (buffer_),
123			direction (buffer_->props.direction),
124			lookup_mask (lookup_mask_),
125			context_length (context_length_),
126			nesting_level_left (nesting_level_left_),
127			lookup_props (0), property (0), debug_depth (0) {}
128
129  void set_lookup (const Lookup &l) {
130    lookup_props = l.get_props ();
131  }
132
133  struct mark_skipping_forward_iterator_t
134  {
135    inline mark_skipping_forward_iterator_t (hb_apply_context_t *c_,
136					     unsigned int start_index_,
137					     unsigned int num_items_)
138    {
139      c = c_;
140      idx = start_index_;
141      num_items = num_items_;
142      end = MIN (c->buffer->len, c->buffer->idx + c->context_length);
143    }
144    inline bool has_no_chance (void) const
145    {
146      return unlikely (num_items && idx + num_items >= end);
147    }
148    inline bool next (unsigned int *property_out,
149		      unsigned int lookup_props)
150    {
151      assert (num_items > 0);
152      do
153      {
154	if (has_no_chance ())
155	  return false;
156	idx++;
157      } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[idx], lookup_props, property_out));
158      num_items--;
159      return true;
160    }
161    inline bool next (unsigned int *property_out = NULL)
162    {
163      return next (property_out, c->lookup_props);
164    }
165
166    unsigned int idx;
167    private:
168    hb_apply_context_t *c;
169    unsigned int num_items;
170    unsigned int end;
171  };
172
173  struct mark_skipping_backward_iterator_t
174  {
175    inline mark_skipping_backward_iterator_t (hb_apply_context_t *c_,
176					      unsigned int start_index_,
177					      unsigned int num_items_)
178    {
179      c = c_;
180      idx = start_index_;
181      num_items = num_items_;
182    }
183    inline bool has_no_chance (void) const
184    {
185      return unlikely (idx < num_items);
186    }
187    inline bool prev (unsigned int *property_out,
188		      unsigned int lookup_props)
189    {
190      assert (num_items > 0);
191      do
192      {
193	if (has_no_chance ())
194	  return false;
195	idx--;
196      } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->out_info[idx], lookup_props, property_out));
197      num_items--;
198      return true;
199    }
200    inline bool prev (unsigned int *property_out = NULL)
201    {
202      return prev (property_out, c->lookup_props);
203    }
204
205    unsigned int idx;
206    private:
207    hb_apply_context_t *c;
208    unsigned int num_items;
209  };
210
211  inline bool should_mark_skip_current_glyph (void) const
212  {
213    return _hb_ot_layout_skip_mark (face, &buffer->info[buffer->idx], lookup_props, NULL);
214  }
215
216
217
218  inline void replace_glyph (hb_codepoint_t glyph_index) const
219  {
220    clear_property ();
221    buffer->replace_glyph (glyph_index);
222  }
223  inline void replace_glyphs_be16 (unsigned int num_in,
224				   unsigned int num_out,
225				   const uint16_t *glyph_data_be) const
226  {
227    clear_property ();
228    buffer->replace_glyphs_be16 (num_in, num_out, glyph_data_be);
229  }
230
231  inline void guess_glyph_class (unsigned int klass)
232  {
233    /* XXX if ! has gdef */
234    buffer->info[buffer->idx].props_cache() = klass;
235  }
236
237  private:
238  inline void clear_property (void) const
239  {
240    /* XXX if has gdef */
241    buffer->info[buffer->idx].props_cache() = 0;
242  }
243};
244
245
246
247typedef bool (*intersects_func_t) (hb_set_t *glyphs, const USHORT &value, const void *data);
248typedef bool (*match_func_t) (hb_codepoint_t glyph_id, const USHORT &value, const void *data);
249typedef void (*closure_lookup_func_t) (hb_closure_context_t *c, unsigned int lookup_index);
250typedef bool (*apply_lookup_func_t) (hb_apply_context_t *c, unsigned int lookup_index);
251
252struct ContextClosureFuncs
253{
254  intersects_func_t intersects;
255  closure_lookup_func_t closure;
256};
257struct ContextApplyFuncs
258{
259  match_func_t match;
260  apply_lookup_func_t apply;
261};
262
263static inline bool intersects_glyph (hb_set_t *glyphs, const USHORT &value, const void *data HB_UNUSED)
264{
265  return glyphs->has (value);
266}
267static inline bool intersects_class (hb_set_t *glyphs, const USHORT &value, const void *data)
268{
269  const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data);
270  return class_def.intersects_class (glyphs, value);
271}
272static inline bool intersects_coverage (hb_set_t *glyphs, const USHORT &value, const void *data)
273{
274  const OffsetTo<Coverage> &coverage = (const OffsetTo<Coverage>&)value;
275  return (data+coverage).intersects (glyphs);
276}
277
278static inline bool intersects_array (hb_closure_context_t *c,
279				     unsigned int count,
280				     const USHORT values[],
281				     intersects_func_t intersects_func,
282				     const void *intersects_data)
283{
284  for (unsigned int i = 0; i < count; i++)
285    if (likely (!intersects_func (c->glyphs, values[i], intersects_data)))
286      return false;
287  return true;
288}
289
290
291static inline bool match_glyph (hb_codepoint_t glyph_id, const USHORT &value, const void *data HB_UNUSED)
292{
293  return glyph_id == value;
294}
295static inline bool match_class (hb_codepoint_t glyph_id, const USHORT &value, const void *data)
296{
297  const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data);
298  return class_def.get_class (glyph_id) == value;
299}
300static inline bool match_coverage (hb_codepoint_t glyph_id, const USHORT &value, const void *data)
301{
302  const OffsetTo<Coverage> &coverage = (const OffsetTo<Coverage>&)value;
303  return (data+coverage).get_coverage (glyph_id) != NOT_COVERED;
304}
305
306
307static inline bool match_input (hb_apply_context_t *c,
308				unsigned int count, /* Including the first glyph (not matched) */
309				const USHORT input[], /* Array of input values--start with second glyph */
310				match_func_t match_func,
311				const void *match_data,
312				unsigned int *context_length_out)
313{
314  hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, count - 1);
315  if (skippy_iter.has_no_chance ())
316    return false;
317
318  for (unsigned int i = 1; i < count; i++)
319  {
320    if (!skippy_iter.next ())
321      return false;
322
323    if (likely (!match_func (c->buffer->info[skippy_iter.idx].codepoint, input[i - 1], match_data)))
324      return false;
325  }
326
327  *context_length_out = skippy_iter.idx - c->buffer->idx + 1;
328
329  return true;
330}
331
332static inline bool match_backtrack (hb_apply_context_t *c,
333				    unsigned int count,
334				    const USHORT backtrack[],
335				    match_func_t match_func,
336				    const void *match_data)
337{
338  hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->backtrack_len (), count);
339  if (skippy_iter.has_no_chance ())
340    return false;
341
342  for (unsigned int i = 0; i < count; i++)
343  {
344    if (!skippy_iter.prev ())
345      return false;
346
347    if (likely (!match_func (c->buffer->out_info[skippy_iter.idx].codepoint, backtrack[i], match_data)))
348      return false;
349  }
350
351  return true;
352}
353
354static inline bool match_lookahead (hb_apply_context_t *c,
355				    unsigned int count,
356				    const USHORT lookahead[],
357				    match_func_t match_func,
358				    const void *match_data,
359				    unsigned int offset)
360{
361  hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx + offset - 1, count);
362  if (skippy_iter.has_no_chance ())
363    return false;
364
365  for (unsigned int i = 0; i < count; i++)
366  {
367    if (!skippy_iter.next ())
368      return false;
369
370    if (likely (!match_func (c->buffer->info[skippy_iter.idx].codepoint, lookahead[i], match_data)))
371      return false;
372  }
373
374  return true;
375}
376
377
378
379struct LookupRecord
380{
381  inline bool sanitize (hb_sanitize_context_t *c) {
382    TRACE_SANITIZE ();
383    return TRACE_RETURN (c->check_struct (this));
384  }
385
386  USHORT	sequenceIndex;		/* Index into current glyph
387					 * sequence--first glyph = 0 */
388  USHORT	lookupListIndex;	/* Lookup to apply to that
389					 * position--zero--based */
390  public:
391  DEFINE_SIZE_STATIC (4);
392};
393
394
395static inline void closure_lookup (hb_closure_context_t *c,
396				   unsigned int lookupCount,
397				   const LookupRecord lookupRecord[], /* Array of LookupRecords--in design order */
398				   closure_lookup_func_t closure_func)
399{
400  for (unsigned int i = 0; i < lookupCount; i++)
401    closure_func (c, lookupRecord->lookupListIndex);
402}
403
404static inline bool apply_lookup (hb_apply_context_t *c,
405				 unsigned int count, /* Including the first glyph */
406				 unsigned int lookupCount,
407				 const LookupRecord lookupRecord[], /* Array of LookupRecords--in design order */
408				 apply_lookup_func_t apply_func)
409{
410  unsigned int end = MIN (c->buffer->len, c->buffer->idx + c->context_length);
411  if (unlikely (count == 0 || c->buffer->idx + count > end))
412    return false;
413
414  /* TODO We don't support lookupRecord arrays that are not increasing:
415   *      Should be easy for in_place ones at least. */
416
417  /* Note: If sublookup is reverse, it will underflow after the first loop
418   * and we jump out of it.  Not entirely disastrous.  So we don't check
419   * for reverse lookup here.
420   */
421  for (unsigned int i = 0; i < count; /* NOP */)
422  {
423    if (unlikely (c->buffer->idx == end))
424      return true;
425    while (c->should_mark_skip_current_glyph ())
426    {
427      /* No lookup applied for this index */
428      c->buffer->next_glyph ();
429      if (unlikely (c->buffer->idx == end))
430	return true;
431    }
432
433    if (lookupCount && i == lookupRecord->sequenceIndex)
434    {
435      unsigned int old_pos = c->buffer->idx;
436
437      /* Apply a lookup */
438      bool done = apply_func (c, lookupRecord->lookupListIndex);
439
440      lookupRecord++;
441      lookupCount--;
442      /* Err, this is wrong if the lookup jumped over some glyphs */
443      i += c->buffer->idx - old_pos;
444      if (unlikely (c->buffer->idx == end))
445	return true;
446
447      if (!done)
448	goto not_applied;
449    }
450    else
451    {
452    not_applied:
453      /* No lookup applied for this index */
454      c->buffer->next_glyph ();
455      i++;
456    }
457  }
458
459  return true;
460}
461
462
463
464/* Contextual lookups */
465
466struct ContextClosureLookupContext
467{
468  ContextClosureFuncs funcs;
469  const void *intersects_data;
470};
471
472struct ContextApplyLookupContext
473{
474  ContextApplyFuncs funcs;
475  const void *match_data;
476};
477
478static inline void context_closure_lookup (hb_closure_context_t *c,
479					   unsigned int inputCount, /* Including the first glyph (not matched) */
480					   const USHORT input[], /* Array of input values--start with second glyph */
481					   unsigned int lookupCount,
482					   const LookupRecord lookupRecord[],
483					   ContextClosureLookupContext &lookup_context)
484{
485  if (intersects_array (c,
486			inputCount ? inputCount - 1 : 0, input,
487			lookup_context.funcs.intersects, lookup_context.intersects_data))
488    closure_lookup (c,
489		    lookupCount, lookupRecord,
490		    lookup_context.funcs.closure);
491}
492
493
494static inline bool context_apply_lookup (hb_apply_context_t *c,
495					 unsigned int inputCount, /* Including the first glyph (not matched) */
496					 const USHORT input[], /* Array of input values--start with second glyph */
497					 unsigned int lookupCount,
498					 const LookupRecord lookupRecord[],
499					 ContextApplyLookupContext &lookup_context)
500{
501  hb_apply_context_t new_context = *c;
502  return match_input (c,
503		      inputCount, input,
504		      lookup_context.funcs.match, lookup_context.match_data,
505		      &new_context.context_length)
506      && apply_lookup (&new_context,
507		       inputCount,
508		       lookupCount, lookupRecord,
509		       lookup_context.funcs.apply);
510}
511
512struct Rule
513{
514  friend struct RuleSet;
515
516  private:
517
518  inline void closure (hb_closure_context_t *c, ContextClosureLookupContext &lookup_context) const
519  {
520    TRACE_CLOSURE ();
521    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].static_size * (inputCount ? inputCount - 1 : 0));
522    context_closure_lookup (c,
523			    inputCount, input,
524			    lookupCount, lookupRecord,
525			    lookup_context);
526  }
527
528  inline bool apply (hb_apply_context_t *c, ContextApplyLookupContext &lookup_context) const
529  {
530    TRACE_APPLY ();
531    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].static_size * (inputCount ? inputCount - 1 : 0));
532    return TRACE_RETURN (context_apply_lookup (c, inputCount, input, lookupCount, lookupRecord, lookup_context));
533  }
534
535  public:
536  inline bool sanitize (hb_sanitize_context_t *c) {
537    TRACE_SANITIZE ();
538    return inputCount.sanitize (c)
539	&& lookupCount.sanitize (c)
540	&& c->check_range (input,
541			   input[0].static_size * inputCount
542			   + lookupRecordX[0].static_size * lookupCount);
543  }
544
545  private:
546  USHORT	inputCount;		/* Total number of glyphs in input
547					 * glyph sequence--includes the first
548					 * glyph */
549  USHORT	lookupCount;		/* Number of LookupRecords */
550  USHORT	input[VAR];		/* Array of match inputs--start with
551					 * second glyph */
552  LookupRecord	lookupRecordX[VAR];	/* Array of LookupRecords--in
553					 * design order */
554  public:
555  DEFINE_SIZE_ARRAY2 (4, input, lookupRecordX);
556};
557
558struct RuleSet
559{
560  inline void closure (hb_closure_context_t *c, ContextClosureLookupContext &lookup_context) const
561  {
562    TRACE_CLOSURE ();
563    unsigned int num_rules = rule.len;
564    for (unsigned int i = 0; i < num_rules; i++)
565      (this+rule[i]).closure (c, lookup_context);
566  }
567
568  inline bool apply (hb_apply_context_t *c, ContextApplyLookupContext &lookup_context) const
569  {
570    TRACE_APPLY ();
571    unsigned int num_rules = rule.len;
572    for (unsigned int i = 0; i < num_rules; i++)
573    {
574      if ((this+rule[i]).apply (c, lookup_context))
575        return TRACE_RETURN (true);
576    }
577    return TRACE_RETURN (false);
578  }
579
580  inline bool sanitize (hb_sanitize_context_t *c) {
581    TRACE_SANITIZE ();
582    return TRACE_RETURN (rule.sanitize (c, this));
583  }
584
585  private:
586  OffsetArrayOf<Rule>
587		rule;			/* Array of Rule tables
588					 * ordered by preference */
589  public:
590  DEFINE_SIZE_ARRAY (2, rule);
591};
592
593
594struct ContextFormat1
595{
596  friend struct Context;
597
598  private:
599
600  inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
601  {
602    TRACE_CLOSURE ();
603
604    const Coverage &cov = (this+coverage);
605
606    struct ContextClosureLookupContext lookup_context = {
607      {intersects_glyph, closure_func},
608      NULL
609    };
610
611    unsigned int count = ruleSet.len;
612    for (unsigned int i = 0; i < count; i++)
613      if (cov.intersects_coverage (c->glyphs, i)) {
614	const RuleSet &rule_set = this+ruleSet[i];
615	rule_set.closure (c, lookup_context);
616      }
617  }
618
619  inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
620  {
621    TRACE_APPLY ();
622    unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
623    if (likely (index == NOT_COVERED))
624      return TRACE_RETURN (false);
625
626    const RuleSet &rule_set = this+ruleSet[index];
627    struct ContextApplyLookupContext lookup_context = {
628      {match_glyph, apply_func},
629      NULL
630    };
631    return TRACE_RETURN (rule_set.apply (c, lookup_context));
632  }
633
634  inline bool sanitize (hb_sanitize_context_t *c) {
635    TRACE_SANITIZE ();
636    return TRACE_RETURN (coverage.sanitize (c, this) && ruleSet.sanitize (c, this));
637  }
638
639  private:
640  USHORT	format;			/* Format identifier--format = 1 */
641  OffsetTo<Coverage>
642		coverage;		/* Offset to Coverage table--from
643					 * beginning of table */
644  OffsetArrayOf<RuleSet>
645		ruleSet;		/* Array of RuleSet tables
646					 * ordered by Coverage Index */
647  public:
648  DEFINE_SIZE_ARRAY (6, ruleSet);
649};
650
651
652struct ContextFormat2
653{
654  friend struct Context;
655
656  private:
657
658  inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
659  {
660    TRACE_CLOSURE ();
661    if (!(this+coverage).intersects (c->glyphs))
662      return;
663
664    const ClassDef &class_def = this+classDef;
665
666    struct ContextClosureLookupContext lookup_context = {
667      {intersects_class, closure_func},
668      NULL
669    };
670
671    unsigned int count = ruleSet.len;
672    for (unsigned int i = 0; i < count; i++)
673      if (class_def.intersects_class (c->glyphs, i)) {
674	const RuleSet &rule_set = this+ruleSet[i];
675	rule_set.closure (c, lookup_context);
676      }
677  }
678
679  inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
680  {
681    TRACE_APPLY ();
682    unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
683    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
684
685    const ClassDef &class_def = this+classDef;
686    index = class_def (c->buffer->info[c->buffer->idx].codepoint);
687    const RuleSet &rule_set = this+ruleSet[index];
688    struct ContextApplyLookupContext lookup_context = {
689      {match_class, apply_func},
690      &class_def
691    };
692    return TRACE_RETURN (rule_set.apply (c, lookup_context));
693  }
694
695  inline bool sanitize (hb_sanitize_context_t *c) {
696    TRACE_SANITIZE ();
697    return TRACE_RETURN (coverage.sanitize (c, this) && classDef.sanitize (c, this) && ruleSet.sanitize (c, this));
698  }
699
700  private:
701  USHORT	format;			/* Format identifier--format = 2 */
702  OffsetTo<Coverage>
703		coverage;		/* Offset to Coverage table--from
704					 * beginning of table */
705  OffsetTo<ClassDef>
706		classDef;		/* Offset to glyph ClassDef table--from
707					 * beginning of table */
708  OffsetArrayOf<RuleSet>
709		ruleSet;		/* Array of RuleSet tables
710					 * ordered by class */
711  public:
712  DEFINE_SIZE_ARRAY (8, ruleSet);
713};
714
715
716struct ContextFormat3
717{
718  friend struct Context;
719
720  private:
721
722  inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
723  {
724    TRACE_CLOSURE ();
725    if (!(this+coverage[0]).intersects (c->glyphs))
726      return;
727
728    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * glyphCount);
729    struct ContextClosureLookupContext lookup_context = {
730      {intersects_coverage, closure_func},
731      this
732    };
733    context_closure_lookup (c,
734			    glyphCount, (const USHORT *) (coverage + 1),
735			    lookupCount, lookupRecord,
736			    lookup_context);
737  }
738
739  inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
740  {
741    TRACE_APPLY ();
742    unsigned int index = (this+coverage[0]) (c->buffer->info[c->buffer->idx].codepoint);
743    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
744
745    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * glyphCount);
746    struct ContextApplyLookupContext lookup_context = {
747      {match_coverage, apply_func},
748      this
749    };
750    return TRACE_RETURN (context_apply_lookup (c, glyphCount, (const USHORT *) (coverage + 1), lookupCount, lookupRecord, lookup_context));
751  }
752
753  inline bool sanitize (hb_sanitize_context_t *c) {
754    TRACE_SANITIZE ();
755    if (!c->check_struct (this)) return TRACE_RETURN (false);
756    unsigned int count = glyphCount;
757    if (!c->check_array (coverage, coverage[0].static_size, count)) return TRACE_RETURN (false);
758    for (unsigned int i = 0; i < count; i++)
759      if (!coverage[i].sanitize (c, this)) return TRACE_RETURN (false);
760    LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * count);
761    return TRACE_RETURN (c->check_array (lookupRecord, lookupRecord[0].static_size, lookupCount));
762  }
763
764  private:
765  USHORT	format;			/* Format identifier--format = 3 */
766  USHORT	glyphCount;		/* Number of glyphs in the input glyph
767					 * sequence */
768  USHORT	lookupCount;		/* Number of LookupRecords */
769  OffsetTo<Coverage>
770		coverage[VAR];		/* Array of offsets to Coverage
771					 * table in glyph sequence order */
772  LookupRecord	lookupRecordX[VAR];	/* Array of LookupRecords--in
773					 * design order */
774  public:
775  DEFINE_SIZE_ARRAY2 (6, coverage, lookupRecordX);
776};
777
778struct Context
779{
780  protected:
781
782  inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
783  {
784    TRACE_CLOSURE ();
785    switch (u.format) {
786    case 1: u.format1.closure (c, closure_func); break;
787    case 2: u.format2.closure (c, closure_func); break;
788    case 3: u.format3.closure (c, closure_func); break;
789    default:                                     break;
790    }
791  }
792
793  inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
794  {
795    TRACE_APPLY ();
796    switch (u.format) {
797    case 1: return TRACE_RETURN (u.format1.apply (c, apply_func));
798    case 2: return TRACE_RETURN (u.format2.apply (c, apply_func));
799    case 3: return TRACE_RETURN (u.format3.apply (c, apply_func));
800    default:return TRACE_RETURN (false);
801    }
802  }
803
804  inline bool sanitize (hb_sanitize_context_t *c) {
805    TRACE_SANITIZE ();
806    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
807    switch (u.format) {
808    case 1: return TRACE_RETURN (u.format1.sanitize (c));
809    case 2: return TRACE_RETURN (u.format2.sanitize (c));
810    case 3: return TRACE_RETURN (u.format3.sanitize (c));
811    default:return TRACE_RETURN (true);
812    }
813  }
814
815  private:
816  union {
817  USHORT		format;		/* Format identifier */
818  ContextFormat1	format1;
819  ContextFormat2	format2;
820  ContextFormat3	format3;
821  } u;
822};
823
824
825/* Chaining Contextual lookups */
826
827struct ChainContextClosureLookupContext
828{
829  ContextClosureFuncs funcs;
830  const void *intersects_data[3];
831};
832
833struct ChainContextApplyLookupContext
834{
835  ContextApplyFuncs funcs;
836  const void *match_data[3];
837};
838
839static inline void chain_context_closure_lookup (hb_closure_context_t *c,
840						 unsigned int backtrackCount,
841						 const USHORT backtrack[],
842						 unsigned int inputCount, /* Including the first glyph (not matched) */
843						 const USHORT input[], /* Array of input values--start with second glyph */
844						 unsigned int lookaheadCount,
845						 const USHORT lookahead[],
846						 unsigned int lookupCount,
847						 const LookupRecord lookupRecord[],
848						 ChainContextClosureLookupContext &lookup_context)
849{
850  if (intersects_array (c,
851			backtrackCount, backtrack,
852			lookup_context.funcs.intersects, lookup_context.intersects_data[0])
853   && intersects_array (c,
854			inputCount ? inputCount - 1 : 0, input,
855			lookup_context.funcs.intersects, lookup_context.intersects_data[1])
856  && intersects_array (c,
857		       lookaheadCount, lookahead,
858		       lookup_context.funcs.intersects, lookup_context.intersects_data[2]))
859    closure_lookup (c,
860		    lookupCount, lookupRecord,
861		    lookup_context.funcs.closure);
862}
863
864static inline bool chain_context_apply_lookup (hb_apply_context_t *c,
865					       unsigned int backtrackCount,
866					       const USHORT backtrack[],
867					       unsigned int inputCount, /* Including the first glyph (not matched) */
868					       const USHORT input[], /* Array of input values--start with second glyph */
869					       unsigned int lookaheadCount,
870					       const USHORT lookahead[],
871					       unsigned int lookupCount,
872					       const LookupRecord lookupRecord[],
873					       ChainContextApplyLookupContext &lookup_context)
874{
875  /* First guess */
876  if (unlikely (c->buffer->backtrack_len () < backtrackCount ||
877		c->buffer->idx + inputCount + lookaheadCount > c->buffer->len ||
878		inputCount + lookaheadCount > c->context_length))
879    return false;
880
881  hb_apply_context_t new_context = *c;
882  return match_backtrack (c,
883			  backtrackCount, backtrack,
884			  lookup_context.funcs.match, lookup_context.match_data[0])
885      && match_input (c,
886		      inputCount, input,
887		      lookup_context.funcs.match, lookup_context.match_data[1],
888		      &new_context.context_length)
889      && match_lookahead (c,
890			  lookaheadCount, lookahead,
891			  lookup_context.funcs.match, lookup_context.match_data[2],
892			  new_context.context_length)
893      && apply_lookup (&new_context,
894		       inputCount,
895		       lookupCount, lookupRecord,
896		       lookup_context.funcs.apply);
897}
898
899struct ChainRule
900{
901  friend struct ChainRuleSet;
902
903  private:
904
905  inline void closure (hb_closure_context_t *c, ChainContextClosureLookupContext &lookup_context) const
906  {
907    TRACE_CLOSURE ();
908    const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
909    const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
910    const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
911    chain_context_closure_lookup (c,
912				  backtrack.len, backtrack.array,
913				  input.len, input.array,
914				  lookahead.len, lookahead.array,
915				  lookup.len, lookup.array,
916				  lookup_context);
917  }
918
919  inline bool apply (hb_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
920  {
921    TRACE_APPLY ();
922    const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
923    const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
924    const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
925    return TRACE_RETURN (chain_context_apply_lookup (c,
926						     backtrack.len, backtrack.array,
927						     input.len, input.array,
928						     lookahead.len, lookahead.array, lookup.len,
929						     lookup.array, lookup_context));
930  }
931
932  public:
933  inline bool sanitize (hb_sanitize_context_t *c) {
934    TRACE_SANITIZE ();
935    if (!backtrack.sanitize (c)) return TRACE_RETURN (false);
936    HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
937    if (!input.sanitize (c)) return TRACE_RETURN (false);
938    ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
939    if (!lookahead.sanitize (c)) return TRACE_RETURN (false);
940    ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
941    return TRACE_RETURN (lookup.sanitize (c));
942  }
943
944  private:
945  ArrayOf<USHORT>
946		backtrack;		/* Array of backtracking values
947					 * (to be matched before the input
948					 * sequence) */
949  HeadlessArrayOf<USHORT>
950		inputX;			/* Array of input values (start with
951					 * second glyph) */
952  ArrayOf<USHORT>
953		lookaheadX;		/* Array of lookahead values's (to be
954					 * matched after the input sequence) */
955  ArrayOf<LookupRecord>
956		lookupX;		/* Array of LookupRecords--in
957					 * design order) */
958  public:
959  DEFINE_SIZE_MIN (8);
960};
961
962struct ChainRuleSet
963{
964  inline void closure (hb_closure_context_t *c, ChainContextClosureLookupContext &lookup_context) const
965  {
966    TRACE_CLOSURE ();
967    unsigned int num_rules = rule.len;
968    for (unsigned int i = 0; i < num_rules; i++)
969      (this+rule[i]).closure (c, lookup_context);
970  }
971
972  inline bool apply (hb_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
973  {
974    TRACE_APPLY ();
975    unsigned int num_rules = rule.len;
976    for (unsigned int i = 0; i < num_rules; i++)
977      if ((this+rule[i]).apply (c, lookup_context))
978        return TRACE_RETURN (true);
979
980    return TRACE_RETURN (false);
981  }
982
983  inline bool sanitize (hb_sanitize_context_t *c) {
984    TRACE_SANITIZE ();
985    return TRACE_RETURN (rule.sanitize (c, this));
986  }
987
988  private:
989  OffsetArrayOf<ChainRule>
990		rule;			/* Array of ChainRule tables
991					 * ordered by preference */
992  public:
993  DEFINE_SIZE_ARRAY (2, rule);
994};
995
996struct ChainContextFormat1
997{
998  friend struct ChainContext;
999
1000  private:
1001
1002  inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
1003  {
1004    TRACE_CLOSURE ();
1005    const Coverage &cov = (this+coverage);
1006
1007    struct ChainContextClosureLookupContext lookup_context = {
1008      {intersects_glyph, closure_func},
1009      {NULL, NULL, NULL}
1010    };
1011
1012    unsigned int count = ruleSet.len;
1013    for (unsigned int i = 0; i < count; i++)
1014      if (cov.intersects_coverage (c->glyphs, i)) {
1015	const ChainRuleSet &rule_set = this+ruleSet[i];
1016	rule_set.closure (c, lookup_context);
1017      }
1018  }
1019
1020  inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
1021  {
1022    TRACE_APPLY ();
1023    unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
1024    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
1025
1026    const ChainRuleSet &rule_set = this+ruleSet[index];
1027    struct ChainContextApplyLookupContext lookup_context = {
1028      {match_glyph, apply_func},
1029      {NULL, NULL, NULL}
1030    };
1031    return TRACE_RETURN (rule_set.apply (c, lookup_context));
1032  }
1033
1034  inline bool sanitize (hb_sanitize_context_t *c) {
1035    TRACE_SANITIZE ();
1036    return TRACE_RETURN (coverage.sanitize (c, this) && ruleSet.sanitize (c, this));
1037  }
1038
1039  private:
1040  USHORT	format;			/* Format identifier--format = 1 */
1041  OffsetTo<Coverage>
1042		coverage;		/* Offset to Coverage table--from
1043					 * beginning of table */
1044  OffsetArrayOf<ChainRuleSet>
1045		ruleSet;		/* Array of ChainRuleSet tables
1046					 * ordered by Coverage Index */
1047  public:
1048  DEFINE_SIZE_ARRAY (6, ruleSet);
1049};
1050
1051struct ChainContextFormat2
1052{
1053  friend struct ChainContext;
1054
1055  private:
1056
1057  inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
1058  {
1059    TRACE_CLOSURE ();
1060    if (!(this+coverage).intersects (c->glyphs))
1061      return;
1062
1063    const ClassDef &backtrack_class_def = this+backtrackClassDef;
1064    const ClassDef &input_class_def = this+inputClassDef;
1065    const ClassDef &lookahead_class_def = this+lookaheadClassDef;
1066
1067    struct ChainContextClosureLookupContext lookup_context = {
1068      {intersects_class, closure_func},
1069      {&backtrack_class_def,
1070       &input_class_def,
1071       &lookahead_class_def}
1072    };
1073
1074    unsigned int count = ruleSet.len;
1075    for (unsigned int i = 0; i < count; i++)
1076      if (input_class_def.intersects_class (c->glyphs, i)) {
1077	const ChainRuleSet &rule_set = this+ruleSet[i];
1078	rule_set.closure (c, lookup_context);
1079      }
1080  }
1081
1082  inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
1083  {
1084    TRACE_APPLY ();
1085    unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
1086    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
1087
1088    const ClassDef &backtrack_class_def = this+backtrackClassDef;
1089    const ClassDef &input_class_def = this+inputClassDef;
1090    const ClassDef &lookahead_class_def = this+lookaheadClassDef;
1091
1092    index = input_class_def (c->buffer->info[c->buffer->idx].codepoint);
1093    const ChainRuleSet &rule_set = this+ruleSet[index];
1094    struct ChainContextApplyLookupContext lookup_context = {
1095      {match_class, apply_func},
1096      {&backtrack_class_def,
1097       &input_class_def,
1098       &lookahead_class_def}
1099    };
1100    return TRACE_RETURN (rule_set.apply (c, lookup_context));
1101  }
1102
1103  inline bool sanitize (hb_sanitize_context_t *c) {
1104    TRACE_SANITIZE ();
1105    return TRACE_RETURN (coverage.sanitize (c, this) && backtrackClassDef.sanitize (c, this) &&
1106			 inputClassDef.sanitize (c, this) && lookaheadClassDef.sanitize (c, this) &&
1107			 ruleSet.sanitize (c, this));
1108  }
1109
1110  private:
1111  USHORT	format;			/* Format identifier--format = 2 */
1112  OffsetTo<Coverage>
1113		coverage;		/* Offset to Coverage table--from
1114					 * beginning of table */
1115  OffsetTo<ClassDef>
1116		backtrackClassDef;	/* Offset to glyph ClassDef table
1117					 * containing backtrack sequence
1118					 * data--from beginning of table */
1119  OffsetTo<ClassDef>
1120		inputClassDef;		/* Offset to glyph ClassDef
1121					 * table containing input sequence
1122					 * data--from beginning of table */
1123  OffsetTo<ClassDef>
1124		lookaheadClassDef;	/* Offset to glyph ClassDef table
1125					 * containing lookahead sequence
1126					 * data--from beginning of table */
1127  OffsetArrayOf<ChainRuleSet>
1128		ruleSet;		/* Array of ChainRuleSet tables
1129					 * ordered by class */
1130  public:
1131  DEFINE_SIZE_ARRAY (12, ruleSet);
1132};
1133
1134struct ChainContextFormat3
1135{
1136  friend struct ChainContext;
1137
1138  private:
1139
1140  inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
1141  {
1142    TRACE_CLOSURE ();
1143    const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
1144
1145    if (!(this+input[0]).intersects (c->glyphs))
1146      return;
1147
1148    const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
1149    const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
1150    struct ChainContextClosureLookupContext lookup_context = {
1151      {intersects_coverage, closure_func},
1152      {this, this, this}
1153    };
1154    chain_context_closure_lookup (c,
1155				  backtrack.len, (const USHORT *) backtrack.array,
1156				  input.len, (const USHORT *) input.array + 1,
1157				  lookahead.len, (const USHORT *) lookahead.array,
1158				  lookup.len, lookup.array,
1159				  lookup_context);
1160  }
1161
1162  inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
1163  {
1164    TRACE_APPLY ();
1165    const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
1166
1167    unsigned int index = (this+input[0]) (c->buffer->info[c->buffer->idx].codepoint);
1168    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
1169
1170    const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
1171    const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
1172    struct ChainContextApplyLookupContext lookup_context = {
1173      {match_coverage, apply_func},
1174      {this, this, this}
1175    };
1176    return TRACE_RETURN (chain_context_apply_lookup (c,
1177						     backtrack.len, (const USHORT *) backtrack.array,
1178						     input.len, (const USHORT *) input.array + 1,
1179						     lookahead.len, (const USHORT *) lookahead.array,
1180						     lookup.len, lookup.array, lookup_context));
1181  }
1182
1183  inline bool sanitize (hb_sanitize_context_t *c) {
1184    TRACE_SANITIZE ();
1185    if (!backtrack.sanitize (c, this)) return TRACE_RETURN (false);
1186    OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
1187    if (!input.sanitize (c, this)) return TRACE_RETURN (false);
1188    OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
1189    if (!lookahead.sanitize (c, this)) return TRACE_RETURN (false);
1190    ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
1191    return TRACE_RETURN (lookup.sanitize (c));
1192  }
1193
1194  private:
1195  USHORT	format;			/* Format identifier--format = 3 */
1196  OffsetArrayOf<Coverage>
1197		backtrack;		/* Array of coverage tables
1198					 * in backtracking sequence, in  glyph
1199					 * sequence order */
1200  OffsetArrayOf<Coverage>
1201		inputX		;	/* Array of coverage
1202					 * tables in input sequence, in glyph
1203					 * sequence order */
1204  OffsetArrayOf<Coverage>
1205		lookaheadX;		/* Array of coverage tables
1206					 * in lookahead sequence, in glyph
1207					 * sequence order */
1208  ArrayOf<LookupRecord>
1209		lookupX;		/* Array of LookupRecords--in
1210					 * design order) */
1211  public:
1212  DEFINE_SIZE_MIN (10);
1213};
1214
1215struct ChainContext
1216{
1217  protected:
1218
1219  inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
1220  {
1221    TRACE_CLOSURE ();
1222    switch (u.format) {
1223    case 1: u.format1.closure (c, closure_func); break;
1224    case 2: u.format2.closure (c, closure_func); break;
1225    case 3: u.format3.closure (c, closure_func); break;
1226    default:                                     break;
1227    }
1228  }
1229
1230  inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
1231  {
1232    TRACE_APPLY ();
1233    switch (u.format) {
1234    case 1: return TRACE_RETURN (u.format1.apply (c, apply_func));
1235    case 2: return TRACE_RETURN (u.format2.apply (c, apply_func));
1236    case 3: return TRACE_RETURN (u.format3.apply (c, apply_func));
1237    default:return TRACE_RETURN (false);
1238    }
1239  }
1240
1241  inline bool sanitize (hb_sanitize_context_t *c) {
1242    TRACE_SANITIZE ();
1243    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
1244    switch (u.format) {
1245    case 1: return TRACE_RETURN (u.format1.sanitize (c));
1246    case 2: return TRACE_RETURN (u.format2.sanitize (c));
1247    case 3: return TRACE_RETURN (u.format3.sanitize (c));
1248    default:return TRACE_RETURN (true);
1249    }
1250  }
1251
1252  private:
1253  union {
1254  USHORT		format;	/* Format identifier */
1255  ChainContextFormat1	format1;
1256  ChainContextFormat2	format2;
1257  ChainContextFormat3	format3;
1258  } u;
1259};
1260
1261
1262struct ExtensionFormat1
1263{
1264  friend struct Extension;
1265
1266  protected:
1267  inline unsigned int get_type (void) const { return extensionLookupType; }
1268  inline unsigned int get_offset (void) const { return extensionOffset; }
1269
1270  inline bool sanitize (hb_sanitize_context_t *c) {
1271    TRACE_SANITIZE ();
1272    return TRACE_RETURN (c->check_struct (this));
1273  }
1274
1275  private:
1276  USHORT	format;			/* Format identifier. Set to 1. */
1277  USHORT	extensionLookupType;	/* Lookup type of subtable referenced
1278					 * by ExtensionOffset (i.e. the
1279					 * extension subtable). */
1280  ULONG		extensionOffset;	/* Offset to the extension subtable,
1281					 * of lookup type subtable. */
1282  public:
1283  DEFINE_SIZE_STATIC (8);
1284};
1285
1286struct Extension
1287{
1288  inline unsigned int get_type (void) const
1289  {
1290    switch (u.format) {
1291    case 1: return u.format1.get_type ();
1292    default:return 0;
1293    }
1294  }
1295  inline unsigned int get_offset (void) const
1296  {
1297    switch (u.format) {
1298    case 1: return u.format1.get_offset ();
1299    default:return 0;
1300    }
1301  }
1302
1303  inline bool sanitize (hb_sanitize_context_t *c) {
1304    TRACE_SANITIZE ();
1305    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
1306    switch (u.format) {
1307    case 1: return TRACE_RETURN (u.format1.sanitize (c));
1308    default:return TRACE_RETURN (true);
1309    }
1310  }
1311
1312  private:
1313  union {
1314  USHORT		format;		/* Format identifier */
1315  ExtensionFormat1	format1;
1316  } u;
1317};
1318
1319
1320/*
1321 * GSUB/GPOS Common
1322 */
1323
1324struct GSUBGPOS
1325{
1326  static const hb_tag_t GSUBTag	= HB_OT_TAG_GSUB;
1327  static const hb_tag_t GPOSTag	= HB_OT_TAG_GPOS;
1328
1329  inline unsigned int get_script_count (void) const
1330  { return (this+scriptList).len; }
1331  inline const Tag& get_script_tag (unsigned int i) const
1332  { return (this+scriptList).get_tag (i); }
1333  inline unsigned int get_script_tags (unsigned int start_offset,
1334				       unsigned int *script_count /* IN/OUT */,
1335				       hb_tag_t     *script_tags /* OUT */) const
1336  { return (this+scriptList).get_tags (start_offset, script_count, script_tags); }
1337  inline const Script& get_script (unsigned int i) const
1338  { return (this+scriptList)[i]; }
1339  inline bool find_script_index (hb_tag_t tag, unsigned int *index) const
1340  { return (this+scriptList).find_index (tag, index); }
1341
1342  inline unsigned int get_feature_count (void) const
1343  { return (this+featureList).len; }
1344  inline const Tag& get_feature_tag (unsigned int i) const
1345  { return (this+featureList).get_tag (i); }
1346  inline unsigned int get_feature_tags (unsigned int start_offset,
1347					unsigned int *feature_count /* IN/OUT */,
1348					hb_tag_t     *feature_tags /* OUT */) const
1349  { return (this+featureList).get_tags (start_offset, feature_count, feature_tags); }
1350  inline const Feature& get_feature (unsigned int i) const
1351  { return (this+featureList)[i]; }
1352  inline bool find_feature_index (hb_tag_t tag, unsigned int *index) const
1353  { return (this+featureList).find_index (tag, index); }
1354
1355  inline unsigned int get_lookup_count (void) const
1356  { return (this+lookupList).len; }
1357  inline const Lookup& get_lookup (unsigned int i) const
1358  { return (this+lookupList)[i]; }
1359
1360  inline bool sanitize (hb_sanitize_context_t *c) {
1361    TRACE_SANITIZE ();
1362    return TRACE_RETURN (version.sanitize (c) && likely (version.major == 1) &&
1363			 scriptList.sanitize (c, this) &&
1364			 featureList.sanitize (c, this) &&
1365			 lookupList.sanitize (c, this));
1366  }
1367
1368  protected:
1369  FixedVersion	version;	/* Version of the GSUB/GPOS table--initially set
1370				 * to 0x00010000 */
1371  OffsetTo<ScriptList>
1372		scriptList;  	/* ScriptList table */
1373  OffsetTo<FeatureList>
1374		featureList; 	/* FeatureList table */
1375  OffsetTo<LookupList>
1376		lookupList; 	/* LookupList table */
1377  public:
1378  DEFINE_SIZE_STATIC (10);
1379};
1380
1381
1382
1383#endif /* HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH */
1384