Imported Upstream version 0.9.12
[platform/upstream/harfbuzz.git] / src / hb-ot-layout-gpos-table.hh
index 41168b2..3e43694 100644 (file)
@@ -32,6 +32,8 @@
 #include "hb-ot-layout-gsubgpos-private.hh"
 
 
+namespace OT {
+
 
 /* buffer **position** var allocations */
 #define attach_lookback() var.u16[0] /* number of glyphs to go back to attach this glyph to its base */
@@ -170,12 +172,12 @@ struct ValueFormat : USHORT
   }
 
   inline bool sanitize_value (hb_sanitize_context_t *c, void *base, Value *values) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_range (values, get_size ()) && (!has_device () || sanitize_value_devices (c, base, values)));
   }
 
   inline bool sanitize_values (hb_sanitize_context_t *c, void *base, Value *values, unsigned int count) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     unsigned int len = get_len ();
 
     if (!c->check_array (values, get_size (), count)) return TRACE_RETURN (false);
@@ -193,7 +195,7 @@ struct ValueFormat : USHORT
 
   /* Just sanitize referenced Device tables.  Doesn't check the values themselves. */
   inline bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, void *base, Value *values, unsigned int count, unsigned int stride) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
 
     if (!has_device ()) return TRACE_RETURN (true);
 
@@ -210,9 +212,6 @@ struct ValueFormat : USHORT
 
 struct AnchorFormat1
 {
-  friend struct Anchor;
-
-  private:
   inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id HB_UNUSED,
                          hb_position_t *x, hb_position_t *y) const
   {
@@ -221,7 +220,7 @@ struct AnchorFormat1
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this));
   }
 
@@ -235,9 +234,6 @@ struct AnchorFormat1
 
 struct AnchorFormat2
 {
-  friend struct Anchor;
-
-  private:
   inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id,
                          hb_position_t *x, hb_position_t *y) const
   {
@@ -253,7 +249,7 @@ struct AnchorFormat2
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this));
   }
 
@@ -268,9 +264,6 @@ struct AnchorFormat2
 
 struct AnchorFormat3
 {
-  friend struct Anchor;
-
-  private:
   inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id HB_UNUSED,
                          hb_position_t *x, hb_position_t *y) const
   {
@@ -284,7 +277,7 @@ struct AnchorFormat3
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this));
   }
 
@@ -319,7 +312,7 @@ struct Anchor
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
@@ -343,13 +336,15 @@ struct Anchor
 
 struct AnchorMatrix
 {
-  inline const Anchor& get_anchor (unsigned int row, unsigned int col, unsigned int cols) const {
+  inline const Anchor& get_anchor (unsigned int row, unsigned int col, unsigned int cols, bool *found) const {
+    *found = false;
     if (unlikely (row >= rows || col >= cols)) return Null(Anchor);
+    *found = !matrix[row * cols + col].is_null ();
     return this+matrix[row * cols + col];
   }
 
   inline bool sanitize (hb_sanitize_context_t *c, unsigned int cols) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!c->check_struct (this)) return TRACE_RETURN (false);
     if (unlikely (rows > 0 && cols >= ((unsigned int) -1) / rows)) return TRACE_RETURN (false);
     unsigned int count = rows * cols;
@@ -374,7 +369,7 @@ struct MarkRecord
   friend struct MarkArray;
 
   inline bool sanitize (hb_sanitize_context_t *c, void *base) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && markAnchor.sanitize (c, base));
   }
 
@@ -394,12 +389,16 @@ struct MarkArray : ArrayOf<MarkRecord>    /* Array of MarkRecords--in Coverage orde
                     const AnchorMatrix &anchors, unsigned int class_count,
                     unsigned int glyph_pos) const
   {
-    TRACE_APPLY ();
+    TRACE_APPLY (this);
     const MarkRecord &record = ArrayOf<MarkRecord>::operator[](mark_index);
     unsigned int mark_class = record.klass;
 
     const Anchor& mark_anchor = this + record.markAnchor;
-    const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count);
+    bool found;
+    const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count, &found);
+    /* If this subtable doesn't have an anchor for this base and this class,
+     * return false such that the subsequent subtables have a chance at it. */
+    if (unlikely (!found)) return TRACE_RETURN (false);
 
     hb_position_t mark_x, mark_y, base_x, base_y;
 
@@ -416,7 +415,7 @@ struct MarkArray : ArrayOf<MarkRecord>      /* Array of MarkRecords--in Coverage orde
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (ArrayOf<MarkRecord>::sanitize (c, this));
   }
 };
@@ -426,9 +425,11 @@ struct MarkArray : ArrayOf<MarkRecord>     /* Array of MarkRecords--in Coverage orde
 
 struct SinglePosFormat1
 {
-  friend struct SinglePos;
-
-  private:
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    (this+coverage).add_coverage (c->input);
+  }
 
   inline const Coverage &get_coverage (void) const
   {
@@ -437,8 +438,8 @@ struct SinglePosFormat1
 
   inline bool apply (hb_apply_context_t *c) const
   {
-    TRACE_APPLY ();
-    unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
+    TRACE_APPLY (this);
+    unsigned int index = (this+coverage).get_coverage  (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     valueFormat.apply_value (c->font, c->direction, this,
@@ -449,7 +450,7 @@ struct SinglePosFormat1
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && coverage.sanitize (c, this) && valueFormat.sanitize_value (c, this, values));
   }
 
@@ -469,9 +470,11 @@ struct SinglePosFormat1
 
 struct SinglePosFormat2
 {
-  friend struct SinglePos;
-
-  private:
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    (this+coverage).add_coverage (c->input);
+  }
 
   inline const Coverage &get_coverage (void) const
   {
@@ -480,8 +483,8 @@ struct SinglePosFormat2
 
   inline bool apply (hb_apply_context_t *c) const
   {
-    TRACE_APPLY ();
-    unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
+    TRACE_APPLY (this);
+    unsigned int index = (this+coverage).get_coverage  (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     if (likely (index >= valueCount)) return TRACE_RETURN (false);
@@ -495,7 +498,7 @@ struct SinglePosFormat2
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && coverage.sanitize (c, this) && valueFormat.sanitize_values (c, this, values, valueCount));
   }
 
@@ -515,31 +518,19 @@ struct SinglePosFormat2
 
 struct SinglePos
 {
-  friend struct PosLookupSubTable;
-
-  private:
-
-  inline const Coverage &get_coverage (void) const
+  template <typename context_t>
+  inline typename context_t::return_t process (context_t *c) const
   {
+    TRACE_PROCESS (this);
     switch (u.format) {
-    case 1: return u.format1.get_coverage ();
-    case 2: return u.format2.get_coverage ();
-    default:return Null(Coverage);
-    }
-  }
-
-  inline bool apply (hb_apply_context_t *c) const
-  {
-    TRACE_APPLY ();
-    switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.apply (c));
-    case 2: return TRACE_RETURN (u.format2.apply (c));
-    default:return TRACE_RETURN (false);
+    case 1: return TRACE_RETURN (c->process (u.format1));
+    case 2: return TRACE_RETURN (c->process (u.format2));
+    default:return TRACE_RETURN (c->default_return_value ());
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
@@ -575,17 +566,34 @@ struct PairSet
 {
   friend struct PairPosFormat1;
 
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c,
+                             const ValueFormat *valueFormats) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    unsigned int len1 = valueFormats[0].get_len ();
+    unsigned int len2 = valueFormats[1].get_len ();
+    unsigned int record_size = USHORT::static_size * (1 + len1 + len2);
+
+    const PairValueRecord *record = CastP<PairValueRecord> (array);
+    unsigned int count = len;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      c->input->add (record->secondGlyph);
+      record = &StructAtOffset<PairValueRecord> (record, record_size);
+    }
+  }
+
   inline bool apply (hb_apply_context_t *c,
                     const ValueFormat *valueFormats,
                     unsigned int pos) const
   {
-    TRACE_APPLY ();
+    TRACE_APPLY (this);
     unsigned int len1 = valueFormats[0].get_len ();
     unsigned int len2 = valueFormats[1].get_len ();
     unsigned int record_size = USHORT::static_size * (1 + len1 + len2);
 
-    unsigned int count = len;
     const PairValueRecord *record = CastP<PairValueRecord> (array);
+    unsigned int count = len;
     for (unsigned int i = 0; i < count; i++)
     {
       if (c->buffer->info[pos].codepoint == record->secondGlyph)
@@ -613,7 +621,7 @@ struct PairSet
   };
 
   inline bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!(c->check_struct (this)
        && c->check_array (array, USHORT::static_size * closure->stride, len))) return TRACE_RETURN (false);
 
@@ -633,9 +641,14 @@ struct PairSet
 
 struct PairPosFormat1
 {
-  friend struct PairPos;
-
-  private:
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    (this+coverage).add_coverage (c->input);
+    unsigned int count = pairSet.len;
+    for (unsigned int i = 0; i < count; i++)
+      (this+pairSet[i]).collect_glyphs (c, &valueFormat1);
+  }
 
   inline const Coverage &get_coverage (void) const
   {
@@ -644,11 +657,11 @@ struct PairPosFormat1
 
   inline bool apply (hb_apply_context_t *c) const
   {
-    TRACE_APPLY ();
+    TRACE_APPLY (this);
     hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, 1);
     if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
 
-    unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
+    unsigned int index = (this+coverage).get_coverage  (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     if (!skippy_iter.next ()) return TRACE_RETURN (false);
@@ -657,7 +670,7 @@ struct PairPosFormat1
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
 
     unsigned int len1 = valueFormat1.get_len ();
     unsigned int len2 = valueFormat2.get_len ();
@@ -691,9 +704,23 @@ struct PairPosFormat1
 
 struct PairPosFormat2
 {
-  friend struct PairPos;
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    /* (this+coverage).add_coverage (c->input); // Don't need this. */
 
-  private:
+    /* TODO only add values for pairs that have nonzero adjustments. */
+
+    unsigned int count1 = class1Count;
+    const ClassDef &klass1 = this+classDef1;
+    for (unsigned int i = 0; i < count1; i++)
+      klass1.add_class (c->input, i);
+
+    unsigned int count2 = class2Count;
+    const ClassDef &klass2 = this+classDef2;
+    for (unsigned int i = 0; i < count2; i++)
+      klass2.add_class (c->input, i);
+  }
 
   inline const Coverage &get_coverage (void) const
   {
@@ -702,11 +729,11 @@ struct PairPosFormat2
 
   inline bool apply (hb_apply_context_t *c) const
   {
-    TRACE_APPLY ();
+    TRACE_APPLY (this);
     hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, 1);
     if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
 
-    unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
+    unsigned int index = (this+coverage).get_coverage  (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     if (!skippy_iter.next ()) return TRACE_RETURN (false);
@@ -715,8 +742,8 @@ struct PairPosFormat2
     unsigned int len2 = valueFormat2.get_len ();
     unsigned int record_len = len1 + len2;
 
-    unsigned int klass1 = (this+classDef1) (c->buffer->cur().codepoint);
-    unsigned int klass2 = (this+classDef2) (c->buffer->info[skippy_iter.idx].codepoint);
+    unsigned int klass1 = (this+classDef1).get_class (c->buffer->cur().codepoint);
+    unsigned int klass2 = (this+classDef2).get_class (c->buffer->info[skippy_iter.idx].codepoint);
     if (unlikely (klass1 >= class1Count || klass2 >= class2Count)) return TRACE_RETURN (false);
 
     const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
@@ -733,7 +760,7 @@ struct PairPosFormat2
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!(c->check_struct (this)
        && coverage.sanitize (c, this)
        && classDef1.sanitize (c, this)
@@ -781,31 +808,19 @@ struct PairPosFormat2
 
 struct PairPos
 {
-  friend struct PosLookupSubTable;
-
-  private:
-
-  inline const Coverage &get_coverage (void) const
+  template <typename context_t>
+  inline typename context_t::return_t process (context_t *c) const
   {
+    TRACE_PROCESS (this);
     switch (u.format) {
-    case 1: return u.format1.get_coverage ();
-    case 2: return u.format2.get_coverage ();
-    default:return Null(Coverage);
-    }
-  }
-
-  inline bool apply (hb_apply_context_t *c) const
-  {
-    TRACE_APPLY ();
-    switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.apply (c));
-    case 2: return TRACE_RETURN (u.format2.apply (c));
-    default:return TRACE_RETURN (false);
+    case 1: return TRACE_RETURN (c->process (u.format1));
+    case 2: return TRACE_RETURN (c->process (u.format2));
+    default:return TRACE_RETURN (c->default_return_value ());
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
@@ -828,7 +843,7 @@ struct EntryExitRecord
   friend struct CursivePosFormat1;
 
   inline bool sanitize (hb_sanitize_context_t *c, void *base) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (entryAnchor.sanitize (c, base) && exitAnchor.sanitize (c, base));
   }
 
@@ -847,9 +862,11 @@ struct EntryExitRecord
 
 struct CursivePosFormat1
 {
-  friend struct CursivePos;
-
-  private:
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    (this+coverage).add_coverage (c->input);
+  }
 
   inline const Coverage &get_coverage (void) const
   {
@@ -858,20 +875,20 @@ struct CursivePosFormat1
 
   inline bool apply (hb_apply_context_t *c) const
   {
-    TRACE_APPLY ();
+    TRACE_APPLY (this);
 
     /* We don't handle mark glyphs here. */
-    if (c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK) return TRACE_RETURN (false);
+    if (c->property & HB_OT_LAYOUT_GLYPH_PROPS_MARK) return TRACE_RETURN (false);
 
     hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, 1);
     if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
 
-    const EntryExitRecord &this_record = entryExitRecord[(this+coverage) (c->buffer->cur().codepoint)];
+    const EntryExitRecord &this_record = entryExitRecord[(this+coverage).get_coverage  (c->buffer->cur().codepoint)];
     if (!this_record.exitAnchor) return TRACE_RETURN (false);
 
     if (!skippy_iter.next ()) return TRACE_RETURN (false);
 
-    const EntryExitRecord &next_record = entryExitRecord[(this+coverage) (c->buffer->info[skippy_iter.idx].codepoint)];
+    const EntryExitRecord &next_record = entryExitRecord[(this+coverage).get_coverage  (c->buffer->info[skippy_iter.idx].codepoint)];
     if (!next_record.entryAnchor) return TRACE_RETURN (false);
 
     unsigned int i = c->buffer->idx;
@@ -939,7 +956,7 @@ struct CursivePosFormat1
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (coverage.sanitize (c, this) && entryExitRecord.sanitize (c, this));
   }
 
@@ -957,29 +974,18 @@ struct CursivePosFormat1
 
 struct CursivePos
 {
-  friend struct PosLookupSubTable;
-
-  private:
-
-  inline const Coverage &get_coverage (void) const
+  template <typename context_t>
+  inline typename context_t::return_t process (context_t *c) const
   {
+    TRACE_PROCESS (this);
     switch (u.format) {
-    case 1: return u.format1.get_coverage ();
-    default:return Null(Coverage);
-    }
-  }
-
-  inline bool apply (hb_apply_context_t *c) const
-  {
-    TRACE_APPLY ();
-    switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.apply (c));
-    default:return TRACE_RETURN (false);
+    case 1: return TRACE_RETURN (c->process (u.format1));
+    default:return TRACE_RETURN (c->default_return_value ());
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
@@ -1002,9 +1008,13 @@ typedef AnchorMatrix BaseArray;          /* base-major--
 
 struct MarkBasePosFormat1
 {
-  friend struct MarkBasePos;
-
-  private:
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    (this+markCoverage).add_coverage (c->input);
+    (this+baseCoverage).add_coverage (c->input);
+    /* TODO only add combinations that have nonzero adjustment. */
+  }
 
   inline const Coverage &get_coverage (void) const
   {
@@ -1013,8 +1023,8 @@ struct MarkBasePosFormat1
 
   inline bool apply (hb_apply_context_t *c) const
   {
-    TRACE_APPLY ();
-    unsigned int mark_index = (this+markCoverage) (c->buffer->cur().codepoint);
+    TRACE_APPLY (this);
+    unsigned int mark_index = (this+markCoverage).get_coverage  (c->buffer->cur().codepoint);
     if (likely (mark_index == NOT_COVERED)) return TRACE_RETURN (false);
 
     /* now we search backwards for a non-mark glyph */
@@ -1028,16 +1038,16 @@ struct MarkBasePosFormat1
     } while (1);
 
     /* The following assertion is too strong, so we've disabled it. */
-    if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH)) {/*return TRACE_RETURN (false);*/}
+    if (!(property & HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH)) {/*return TRACE_RETURN (false);*/}
 
-    unsigned int base_index = (this+baseCoverage) (c->buffer->info[skippy_iter.idx].codepoint);
+    unsigned int base_index = (this+baseCoverage).get_coverage  (c->buffer->info[skippy_iter.idx].codepoint);
     if (base_index == NOT_COVERED) return TRACE_RETURN (false);
 
     return TRACE_RETURN ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, skippy_iter.idx));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && markCoverage.sanitize (c, this) && baseCoverage.sanitize (c, this) &&
                         markArray.sanitize (c, this) && baseArray.sanitize (c, this, (unsigned int) classCount));
   }
@@ -1063,29 +1073,18 @@ struct MarkBasePosFormat1
 
 struct MarkBasePos
 {
-  friend struct PosLookupSubTable;
-
-  private:
-
-  inline const Coverage &get_coverage (void) const
+  template <typename context_t>
+  inline typename context_t::return_t process (context_t *c) const
   {
+    TRACE_PROCESS (this);
     switch (u.format) {
-    case 1: return u.format1.get_coverage ();
-    default:return Null(Coverage);
-    }
-  }
-
-  inline bool apply (hb_apply_context_t *c) const
-  {
-    TRACE_APPLY ();
-    switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.apply (c));
-    default:return TRACE_RETURN (false);
+    case 1: return TRACE_RETURN (c->process (u.format1));
+    default:return TRACE_RETURN (c->default_return_value ());
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
@@ -1113,9 +1112,13 @@ typedef OffsetListOf<LigatureAttach> LigatureArray;
 
 struct MarkLigPosFormat1
 {
-  friend struct MarkLigPos;
-
-  private:
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    (this+markCoverage).add_coverage (c->input);
+    (this+ligatureCoverage).add_coverage (c->input);
+    /* TODO only add combinations that have nonzero adjustment. */
+  }
 
   inline const Coverage &get_coverage (void) const
   {
@@ -1124,8 +1127,8 @@ struct MarkLigPosFormat1
 
   inline bool apply (hb_apply_context_t *c) const
   {
-    TRACE_APPLY ();
-    unsigned int mark_index = (this+markCoverage) (c->buffer->cur().codepoint);
+    TRACE_APPLY (this);
+    unsigned int mark_index = (this+markCoverage).get_coverage  (c->buffer->cur().codepoint);
     if (likely (mark_index == NOT_COVERED)) return TRACE_RETURN (false);
 
     /* now we search backwards for a non-mark glyph */
@@ -1134,10 +1137,10 @@ struct MarkLigPosFormat1
     if (!skippy_iter.prev (&property, LookupFlag::IgnoreMarks)) return TRACE_RETURN (false);
 
     /* The following assertion is too strong, so we've disabled it. */
-    if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE)) {/*return TRACE_RETURN (false);*/}
+    if (!(property & HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE)) {/*return TRACE_RETURN (false);*/}
 
     unsigned int j = skippy_iter.idx;
-    unsigned int lig_index = (this+ligatureCoverage) (c->buffer->info[j].codepoint);
+    unsigned int lig_index = (this+ligatureCoverage).get_coverage  (c->buffer->info[j].codepoint);
     if (lig_index == NOT_COVERED) return TRACE_RETURN (false);
 
     const LigatureArray& lig_array = this+ligatureArray;
@@ -1164,7 +1167,7 @@ struct MarkLigPosFormat1
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && markCoverage.sanitize (c, this) && ligatureCoverage.sanitize (c, this) &&
                         markArray.sanitize (c, this) && ligatureArray.sanitize (c, this, (unsigned int) classCount));
   }
@@ -1191,29 +1194,18 @@ struct MarkLigPosFormat1
 
 struct MarkLigPos
 {
-  friend struct PosLookupSubTable;
-
-  private:
-
-  inline const Coverage &get_coverage (void) const
-  {
-    switch (u.format) {
-    case 1: return u.format1.get_coverage ();
-    default:return Null(Coverage);
-    }
-  }
-
-  inline bool apply (hb_apply_context_t *c) const
+  template <typename context_t>
+  inline typename context_t::return_t process (context_t *c) const
   {
-    TRACE_APPLY ();
+    TRACE_PROCESS (this);
     switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.apply (c));
-    default:return TRACE_RETURN (false);
+    case 1: return TRACE_RETURN (c->process (u.format1));
+    default:return TRACE_RETURN (c->default_return_value ());
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
@@ -1236,9 +1228,13 @@ typedef AnchorMatrix Mark2Array; /* mark2-major--
 
 struct MarkMarkPosFormat1
 {
-  friend struct MarkMarkPos;
-
-  private:
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    (this+mark1Coverage).add_coverage (c->input);
+    (this+mark2Coverage).add_coverage (c->input);
+    /* TODO only add combinations that have nonzero adjustment. */
+  }
 
   inline const Coverage &get_coverage (void) const
   {
@@ -1247,8 +1243,8 @@ struct MarkMarkPosFormat1
 
   inline bool apply (hb_apply_context_t *c) const
   {
-    TRACE_APPLY ();
-    unsigned int mark1_index = (this+mark1Coverage) (c->buffer->cur().codepoint);
+    TRACE_APPLY (this);
+    unsigned int mark1_index = (this+mark1Coverage).get_coverage  (c->buffer->cur().codepoint);
     if (likely (mark1_index == NOT_COVERED)) return TRACE_RETURN (false);
 
     /* now we search backwards for a suitable mark glyph until a non-mark glyph */
@@ -1256,7 +1252,7 @@ struct MarkMarkPosFormat1
     hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->idx, 1);
     if (!skippy_iter.prev (&property)) return TRACE_RETURN (false);
 
-    if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK)) return TRACE_RETURN (false);
+    if (!(property & HB_OT_LAYOUT_GLYPH_PROPS_MARK)) return TRACE_RETURN (false);
 
     unsigned int j = skippy_iter.idx;
 
@@ -1281,14 +1277,14 @@ struct MarkMarkPosFormat1
     return TRACE_RETURN (false);
 
     good:
-    unsigned int mark2_index = (this+mark2Coverage) (c->buffer->info[j].codepoint);
+    unsigned int mark2_index = (this+mark2Coverage).get_coverage  (c->buffer->info[j].codepoint);
     if (mark2_index == NOT_COVERED) return TRACE_RETURN (false);
 
     return TRACE_RETURN ((this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && mark1Coverage.sanitize (c, this) &&
                         mark2Coverage.sanitize (c, this) && mark1Array.sanitize (c, this)
                         && mark2Array.sanitize (c, this, (unsigned int) classCount));
@@ -1317,29 +1313,18 @@ struct MarkMarkPosFormat1
 
 struct MarkMarkPos
 {
-  friend struct PosLookupSubTable;
-
-  private:
-
-  inline const Coverage &get_coverage (void) const
+  template <typename context_t>
+  inline typename context_t::return_t process (context_t *c) const
   {
+    TRACE_PROCESS (this);
     switch (u.format) {
-    case 1: return u.format1.get_coverage ();
-    default:return Null(Coverage);
-    }
-  }
-
-  inline bool apply (hb_apply_context_t *c) const
-  {
-    TRACE_APPLY ();
-    switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.apply (c));
-    default:return TRACE_RETURN (false);
+    case 1: return TRACE_RETURN (c->process (u.format1));
+    default:return TRACE_RETURN (c->default_return_value ());
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
@@ -1355,50 +1340,13 @@ struct MarkMarkPos
 };
 
 
-static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index);
-
-struct ContextPos : Context
-{
-  friend struct PosLookupSubTable;
-
-  private:
-  inline bool apply (hb_apply_context_t *c) const
-  {
-    TRACE_APPLY ();
-    return TRACE_RETURN (Context::apply (c, position_lookup));
-  }
-};
-
-struct ChainContextPos : ChainContext
-{
-  friend struct PosLookupSubTable;
-
-  private:
-  inline bool apply (hb_apply_context_t *c) const
-  {
-    TRACE_APPLY ();
-    return TRACE_RETURN (ChainContext::apply (c, position_lookup));
-  }
-};
+struct ContextPos : Context {};
 
+struct ChainContextPos : ChainContext {};
 
-struct ExtensionPos : Extension
+struct ExtensionPos : Extension<ExtensionPos>
 {
-  friend struct PosLookupSubTable;
-
-  private:
-  inline const struct PosLookupSubTable& get_subtable (void) const
-  {
-    unsigned int offset = get_offset ();
-    if (unlikely (!offset)) return Null(PosLookupSubTable);
-    return StructAtOffset<PosLookupSubTable> (this, offset);
-  }
-
-  inline const Coverage &get_coverage (void) const;
-
-  inline bool apply (hb_apply_context_t *c) const;
-
-  inline bool sanitize (hb_sanitize_context_t *c);
+  typedef struct PosLookupSubTable LookupSubTable;
 };
 
 
@@ -1424,41 +1372,26 @@ struct PosLookupSubTable
     Extension          = 9
   };
 
-  inline const Coverage &get_coverage (unsigned int lookup_type) const
-  {
-    switch (lookup_type) {
-    case Single:               return u.single.get_coverage ();
-    case Pair:                 return u.pair.get_coverage ();
-    case Cursive:              return u.cursive.get_coverage ();
-    case MarkBase:             return u.markBase.get_coverage ();
-    case MarkLig:              return u.markLig.get_coverage ();
-    case MarkMark:             return u.markMark.get_coverage ();
-    case Context:              return u.context.get_coverage ();
-    case ChainContext:         return u.chainContext.get_coverage ();
-    case Extension:            return u.extension.get_coverage ();
-    default:                   return Null(Coverage);
-    }
-  }
-
-  inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const
+  template <typename context_t>
+  inline typename context_t::return_t process (context_t *c, unsigned int lookup_type) const
   {
-    TRACE_APPLY ();
+    TRACE_PROCESS (this);
     switch (lookup_type) {
-    case Single:               return TRACE_RETURN (u.single.apply (c));
-    case Pair:                 return TRACE_RETURN (u.pair.apply (c));
-    case Cursive:              return TRACE_RETURN (u.cursive.apply (c));
-    case MarkBase:             return TRACE_RETURN (u.markBase.apply (c));
-    case MarkLig:              return TRACE_RETURN (u.markLig.apply (c));
-    case MarkMark:             return TRACE_RETURN (u.markMark.apply (c));
-    case Context:              return TRACE_RETURN (u.context.apply (c));
-    case ChainContext:         return TRACE_RETURN (u.chainContext.apply (c));
-    case Extension:            return TRACE_RETURN (u.extension.apply (c));
-    default:                   return TRACE_RETURN (false);
+    case Single:               return TRACE_RETURN (u.single.process (c));
+    case Pair:                 return TRACE_RETURN (u.pair.process (c));
+    case Cursive:              return TRACE_RETURN (u.cursive.process (c));
+    case MarkBase:             return TRACE_RETURN (u.markBase.process (c));
+    case MarkLig:              return TRACE_RETURN (u.markLig.process (c));
+    case MarkMark:             return TRACE_RETURN (u.markMark.process (c));
+    case Context:              return TRACE_RETURN (u.context.process (c));
+    case ChainContext:         return TRACE_RETURN (u.chainContext.process (c));
+    case Extension:            return TRACE_RETURN (u.extension.process (c));
+    default:                   return TRACE_RETURN (c->default_return_value ());
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!u.header.sub_format.sanitize (c))
       return TRACE_RETURN (false);
     switch (lookup_type) {
@@ -1478,7 +1411,7 @@ struct PosLookupSubTable
   protected:
   union {
   struct {
-    USHORT                     sub_format;
+    USHORT             sub_format;
   } header;
   SinglePos            single;
   PairPos              pair;
@@ -1500,42 +1433,62 @@ struct PosLookup : Lookup
   inline const PosLookupSubTable& get_subtable (unsigned int i) const
   { return this+CastR<OffsetArrayOf<PosLookupSubTable> > (subTable)[i]; }
 
+  template <typename context_t>
+  inline typename context_t::return_t process (context_t *c) const
+  {
+    TRACE_PROCESS (this);
+    unsigned int lookup_type = get_type ();
+    unsigned int count = get_subtable_count ();
+    for (unsigned int i = 0; i < count; i++) {
+      typename context_t::return_t r = get_subtable (i).process (c, lookup_type);
+      if (c->stop_sublookup_iteration (r))
+        return TRACE_RETURN (r);
+    }
+    return TRACE_RETURN (c->default_return_value ());
+  }
+  template <typename context_t>
+  static inline typename context_t::return_t process_recurse_func (context_t *c, unsigned int lookup_index);
+
+  inline hb_collect_glyphs_context_t::return_t collect_glyphs_lookup (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    c->set_recurse_func (NULL);
+    return TRACE_RETURN (process (c));
+  }
+
   template <typename set_t>
   inline void add_coverage (set_t *glyphs) const
   {
+    hb_get_coverage_context_t c;
     const Coverage *last = NULL;
     unsigned int count = get_subtable_count ();
     for (unsigned int i = 0; i < count; i++) {
-      const Coverage *c = &get_subtable (i).get_coverage (get_type ());
-      if (c != last) {
-        c->add_coverage (glyphs);
-        last = c;
+      const Coverage *coverage = &get_subtable (i).process (&c, get_type ());
+      if (coverage != last) {
+        coverage->add_coverage (glyphs);
+        last = coverage;
       }
     }
   }
 
   inline bool apply_once (hb_apply_context_t *c) const
   {
-    unsigned int lookup_type = get_type ();
-
+    TRACE_APPLY (this);
     if (!c->check_glyph_property (&c->buffer->cur(), c->lookup_props, &c->property))
-      return false;
-
-    unsigned int count = get_subtable_count ();
-    for (unsigned int i = 0; i < count; i++)
-      if (get_subtable (i).apply (c, lookup_type))
-       return true;
-
-    return false;
+      return TRACE_RETURN (false);
+    return TRACE_RETURN (process (c));
   }
 
-  inline bool apply_string (hb_apply_context_t *c) const
+  static bool apply_recurse_func (hb_apply_context_t *c, unsigned int lookup_index);
+
+  inline bool apply_string (hb_apply_context_t *c, const hb_set_digest_t *digest) const
   {
     bool ret = false;
 
-    if (unlikely (!c->buffer->len))
+    if (unlikely (!c->buffer->len || !c->lookup_mask))
       return false;
 
+    c->set_recurse_func (apply_recurse_func);
     c->set_lookup (*this);
 
     c->buffer->idx = 0;
@@ -1543,7 +1496,7 @@ struct PosLookup : Lookup
     while (c->buffer->idx < c->buffer->len)
     {
       if ((c->buffer->cur().mask & c->lookup_mask) &&
-         c->digest.may_have (c->buffer->cur().codepoint) &&
+         digest->may_have (c->buffer->cur().codepoint) &&
          apply_once (c))
        ret = true;
       else
@@ -1554,7 +1507,7 @@ struct PosLookup : Lookup
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (unlikely (!Lookup::sanitize (c))) return TRACE_RETURN (false);
     OffsetArrayOf<PosLookupSubTable> &list = CastR<OffsetArrayOf<PosLookupSubTable> > (subTable);
     return TRACE_RETURN (list.sanitize (c, this, get_type ()));
@@ -1574,18 +1527,11 @@ struct GPOS : GSUBGPOS
   inline const PosLookup& get_lookup (unsigned int i) const
   { return CastR<PosLookup> (GSUBGPOS::get_lookup (i)); }
 
-  template <typename set_t>
-  inline void add_coverage (set_t *glyphs, unsigned int lookup_index) const
-  { get_lookup (lookup_index).add_coverage (glyphs); }
-
-  inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index) const
-  { return get_lookup (lookup_index).apply_string (c); }
-
   static inline void position_start (hb_font_t *font, hb_buffer_t *buffer);
   static inline void position_finish (hb_font_t *font, hb_buffer_t *buffer, hb_bool_t zero_width_attahced_marks);
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (unlikely (!GSUBGPOS::sanitize (c))) return TRACE_RETURN (false);
     OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
     return TRACE_RETURN (list.sanitize (c, this));
@@ -1598,20 +1544,20 @@ struct GPOS : GSUBGPOS
 static void
 fix_cursive_minor_offset (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction)
 {
-    unsigned int j = pos[i].cursive_chain();
-    if (likely (!j))
-      return;
+  unsigned int j = pos[i].cursive_chain();
+  if (likely (!j))
+    return;
 
-    j += i;
+  j += i;
 
-    pos[i].cursive_chain() = 0;
+  pos[i].cursive_chain() = 0;
 
-    fix_cursive_minor_offset (pos, j, direction);
+  fix_cursive_minor_offset (pos, j, direction);
 
-    if (HB_DIRECTION_IS_HORIZONTAL (direction))
-      pos[i].y_offset += pos[j].y_offset;
-    else
-      pos[i].x_offset += pos[j].x_offset;
+  if (HB_DIRECTION_IS_HORIZONTAL (direction))
+    pos[i].y_offset += pos[j].y_offset;
+  else
+    pos[i].x_offset += pos[j].x_offset;
 }
 
 static void
@@ -1674,38 +1620,25 @@ GPOS::position_finish (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer, hb_bool_t
 
 /* Out-of-class implementation for methods recursing */
 
-inline const Coverage & ExtensionPos::get_coverage (void) const
+template <typename context_t>
+inline typename context_t::return_t PosLookup::process_recurse_func (context_t *c, unsigned int lookup_index)
 {
-  return get_subtable ().get_coverage (get_type ());
-}
-
-inline bool ExtensionPos::apply (hb_apply_context_t *c) const
-{
-  TRACE_APPLY ();
-  return TRACE_RETURN (get_subtable ().apply (c, get_type ()));
-}
-
-inline bool ExtensionPos::sanitize (hb_sanitize_context_t *c)
-{
-  TRACE_SANITIZE ();
-  if (unlikely (!Extension::sanitize (c))) return TRACE_RETURN (false);
-  unsigned int offset = get_offset ();
-  if (unlikely (!offset)) return TRACE_RETURN (true);
-  return TRACE_RETURN (StructAtOffset<PosLookupSubTable> (this, offset).sanitize (c, get_type ()));
+  const GPOS &gpos = *(hb_ot_layout_from_face (c->face)->gpos);
+  const PosLookup &l = gpos.get_lookup (lookup_index);
+  return l.process (c);
 }
 
-static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index)
+inline bool PosLookup::apply_recurse_func (hb_apply_context_t *c, unsigned int lookup_index)
 {
   const GPOS &gpos = *(hb_ot_layout_from_face (c->face)->gpos);
   const PosLookup &l = gpos.get_lookup (lookup_index);
-
-  if (unlikely (c->nesting_level_left == 0))
-    return false;
-
-  hb_apply_context_t new_c (*c);
-  new_c.nesting_level_left--;
-  new_c.set_lookup (l);
-  return l.apply_once (&new_c);
+  unsigned int saved_lookup_props = c->lookup_props;
+  unsigned int saved_property = c->property;
+  c->set_lookup (l);
+  bool ret = l.apply_once (c);
+  c->lookup_props = saved_lookup_props;
+  c->property = saved_property;
+  return ret;
 }
 
 
@@ -1713,5 +1646,7 @@ static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_i
 #undef cursive_chain
 
 
+} /* namespace OT */
+
 
 #endif /* HB_OT_LAYOUT_GPOS_TABLE_HH */