[HarfBuzz] harfbuzz-ng: Branch 'master' - 12 commits

Behdad Esfahbod behdad at kemper.freedesktop.org
Mon Apr 23 13:57:52 PDT 2012


 TODO                                     |    3 
 src/Makefile.am                          |    7 
 src/hb-ot-layout-common-private.hh       |  150 ++++++++++
 src/hb-ot-layout-gpos-table.hh           |   27 -
 src/hb-ot-layout-gsub-table.hh           |  422 ++++++++++++++++++++++++++++-
 src/hb-ot-layout-gsubgpos-private.hh     |  438 ++++++++++++++++++++++++++-----
 src/hb-ot-layout-private.hh              |   47 +++
 src/hb-ot-layout.cc                      |   14 
 src/hb-ot-layout.h                       |    8 
 src/hb-ot-shape-complex-indic-private.hh |  264 ++++++++++++++++++
 src/hb-ot-shape-complex-indic.cc         |  227 ----------------
 src/hb-private.hh                        |    4 
 src/hb-shape.cc                          |    2 
 src/indic.cc                             |   46 +++
 14 files changed, 1326 insertions(+), 333 deletions(-)

New commits:
commit 31081f7390e5130df72f89acc609ccab5dc77a48
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Mon Apr 23 16:54:58 2012 -0400

    Implement closure() for Context and ChainContext lookups

diff --git a/src/hb-ot-layout-common-private.hh b/src/hb-ot-layout-common-private.hh
index 410c933..cec339b 100644
--- a/src/hb-ot-layout-common-private.hh
+++ b/src/hb-ot-layout-common-private.hh
@@ -131,6 +131,10 @@ struct RangeRecord
     return c->check_struct (this);
   }
 
+  inline bool intersects (const hb_glyph_map_t *glyphs) const {
+    return glyphs->intersects (start, end);
+  }
+
   GlyphID	start;		/* First GlyphID in the range */
   GlyphID	end;		/* Last GlyphID in the range */
   USHORT	value;		/* Value */
@@ -354,6 +358,10 @@ struct CoverageFormat1
     return glyphArray.sanitize (c);
   }
 
+  inline bool intersects_coverage (const hb_glyph_map_t *glyphs, unsigned int index) const {
+    return glyphs->has (glyphArray[index]);
+  }
+
   struct Iter {
     inline void init (const struct CoverageFormat1 &c_) { c = &c_; i = 0; };
     inline bool more (void) { return i < c->glyphArray.len; }
@@ -394,6 +402,21 @@ struct CoverageFormat2
     return rangeRecord.sanitize (c);
   }
 
+  inline bool intersects_coverage (const hb_glyph_map_t *glyphs, unsigned int index) const {
+    unsigned int i;
+    unsigned int count = rangeRecord.len;
+    for (i = 0; i < count; i++) {
+      const RangeRecord &range = rangeRecord[i];
+      if (range.value <= index &&
+	  index < range.value + (range.end - range.start) &&
+	  range.intersects (glyphs))
+        return true;
+      else if (index < range.value)
+        return false;
+    }
+    return false;
+  }
+
   struct Iter {
     inline void init (const CoverageFormat2 &c_) {
       c = &c_;
@@ -461,7 +484,14 @@ struct Coverage
         return true;
     }
     return false;
+  }
 
+  inline bool intersects_coverage (const hb_glyph_map_t *glyphs, unsigned int index) const {
+    switch (u.format) {
+    case 1: return u.format1.intersects_coverage (glyphs, index);
+    case 2: return u.format2.intersects_coverage (glyphs, index);
+    default:return false;
+    }
   }
 
   struct Iter {
@@ -544,6 +574,14 @@ struct ClassDefFormat1
 	&& classValue.sanitize (c);
   }
 
+  inline bool intersects_class (const hb_glyph_map_t *glyphs, unsigned int klass) const {
+    unsigned int count = classValue.len;
+    for (unsigned int i = 0; i < count; i++)
+      if (classValue[i] == klass && glyphs->has (startGlyph + i))
+        return true;
+    return false;
+  }
+
   USHORT	classFormat;		/* Format identifier--format = 1 */
   GlyphID	startGlyph;		/* First GlyphID of the classValueArray */
   ArrayOf<USHORT>
@@ -570,6 +608,14 @@ struct ClassDefFormat2
     return rangeRecord.sanitize (c);
   }
 
+  inline bool intersects_class (const hb_glyph_map_t *glyphs, unsigned int klass) const {
+    unsigned int count = rangeRecord.len;
+    for (unsigned int i = 0; i < count; i++)
+      if (rangeRecord[i].value == klass && rangeRecord[i].intersects (glyphs))
+        return true;
+    return false;
+  }
+
   USHORT	classFormat;	/* Format identifier--format = 2 */
   SortedArrayOf<RangeRecord>
 		rangeRecord;	/* Array of glyph ranges--ordered by
@@ -601,6 +647,14 @@ struct ClassDef
     }
   }
 
+  inline bool intersects_class (const hb_glyph_map_t *glyphs, unsigned int klass) const {
+    switch (u.format) {
+    case 1: return u.format1.intersects_class (glyphs, klass);
+    case 2: return u.format2.intersects_class (glyphs, klass);
+    default:return false;
+    }
+  }
+
   private:
   union {
   USHORT		format;		/* Format identifier */
diff --git a/src/hb-ot-layout-gsubgpos-private.hh b/src/hb-ot-layout-gsubgpos-private.hh
index 46e322b..10cfd34 100644
--- a/src/hb-ot-layout-gsubgpos-private.hh
+++ b/src/hb-ot-layout-gsubgpos-private.hh
@@ -71,8 +71,6 @@ struct hb_closure_context_t
 			  debug_depth (0) {}
 };
 
-typedef bool (*closure_lookup_func_t) (hb_closure_context_t *c, unsigned int lookup_index);
-
 
 
 #ifndef HB_DEBUG_APPLY
@@ -229,31 +227,63 @@ struct hb_apply_context_t
 
 
 
+typedef bool (*intersects_func_t) (hb_glyph_map_t *glyphs, const USHORT &value, const void *data);
 typedef bool (*match_func_t) (hb_codepoint_t glyph_id, const USHORT &value, const void *data);
+typedef bool (*closure_lookup_func_t) (hb_closure_context_t *c, unsigned int lookup_index);
 typedef bool (*apply_lookup_func_t) (hb_apply_context_t *c, unsigned int lookup_index);
 
-struct ContextFuncs
+struct ContextClosureFuncs
+{
+  intersects_func_t intersects;
+  closure_lookup_func_t closure;
+};
+struct ContextApplyFuncs
 {
   match_func_t match;
   apply_lookup_func_t apply;
 };
 
+static inline bool intersects_glyph (hb_glyph_map_t *glyphs, const USHORT &value, const void *data HB_UNUSED)
+{
+  return glyphs->has (value);
+}
+static inline bool intersects_class (hb_glyph_map_t *glyphs, const USHORT &value, const void *data)
+{
+  const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data);
+  return class_def.intersects_class (glyphs, value);
+}
+static inline bool intersects_coverage (hb_glyph_map_t *glyphs, const USHORT &value, const void *data)
+{
+  const OffsetTo<Coverage> &coverage = (const OffsetTo<Coverage>&)value;
+  return (data+coverage).intersects (glyphs);
+}
+
+static inline bool intersects_array (hb_closure_context_t *c,
+				     unsigned int count,
+				     const USHORT values[],
+				     intersects_func_t intersects_func,
+				     const void *intersects_data)
+{
+  for (unsigned int i = 0; i < count; i++)
+    if (likely (!intersects_func (c->glyphs, values[i], intersects_data)))
+      return false;
+  return true;
+}
+
 
 static inline bool match_glyph (hb_codepoint_t glyph_id, const USHORT &value, const void *data HB_UNUSED)
 {
   return glyph_id == value;
 }
-
 static inline bool match_class (hb_codepoint_t glyph_id, const USHORT &value, const void *data)
 {
   const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data);
   return class_def.get_class (glyph_id) == value;
 }
-
 static inline bool match_coverage (hb_codepoint_t glyph_id, const USHORT &value, const void *data)
 {
   const OffsetTo<Coverage> &coverage = (const OffsetTo<Coverage>&)value;
-  return (data+coverage) (glyph_id) != NOT_COVERED;
+  return (data+coverage).get_coverage (glyph_id) != NOT_COVERED;
 }
 
 
@@ -345,6 +375,16 @@ struct LookupRecord
 };
 
 
+static inline bool closure_lookup (hb_closure_context_t *c,
+				   unsigned int lookupCount,
+				   const LookupRecord lookupRecord[], /* Array of LookupRecords--in design order */
+				   closure_lookup_func_t closure_func)
+{
+  bool ret = false;
+  for (unsigned int i = 0; i < lookupCount; i++)
+    ret = closure_func (c, lookupRecord->lookupListIndex) || ret;
+  return ret;
+}
 
 static inline bool apply_lookup (hb_apply_context_t *c,
 				 unsigned int count, /* Including the first glyph */
@@ -408,18 +448,40 @@ static inline bool apply_lookup (hb_apply_context_t *c,
 
 /* Contextual lookups */
 
-struct ContextLookupContext
+struct ContextClosureLookupContext
+{
+  ContextClosureFuncs funcs;
+  const void *intersects_data;
+};
+
+struct ContextApplyLookupContext
 {
-  ContextFuncs funcs;
+  ContextApplyFuncs funcs;
   const void *match_data;
 };
 
-static inline bool context_lookup (hb_apply_context_t *c,
-				   unsigned int inputCount, /* Including the first glyph (not matched) */
-				   const USHORT input[], /* Array of input values--start with second glyph */
-				   unsigned int lookupCount,
-				   const LookupRecord lookupRecord[],
-				   ContextLookupContext &lookup_context)
+static inline bool context_closure_lookup (hb_closure_context_t *c,
+					   unsigned int inputCount, /* Including the first glyph (not matched) */
+					   const USHORT input[], /* Array of input values--start with second glyph */
+					   unsigned int lookupCount,
+					   const LookupRecord lookupRecord[],
+					   ContextClosureLookupContext &lookup_context)
+{
+  return intersects_array (c,
+			   inputCount ? inputCount - 1 : 0, input,
+			   lookup_context.funcs.intersects, lookup_context.intersects_data)
+      && closure_lookup (c,
+			 lookupCount, lookupRecord,
+			 lookup_context.funcs.closure);
+}
+
+
+static inline bool context_apply_lookup (hb_apply_context_t *c,
+					 unsigned int inputCount, /* Including the first glyph (not matched) */
+					 const USHORT input[], /* Array of input values--start with second glyph */
+					 unsigned int lookupCount,
+					 const LookupRecord lookupRecord[],
+					 ContextApplyLookupContext &lookup_context)
 {
   hb_apply_context_t new_context = *c;
   return match_input (c,
@@ -437,14 +499,25 @@ struct Rule
   friend struct RuleSet;
 
   private:
-  inline bool apply (hb_apply_context_t *c, ContextLookupContext &lookup_context) const
+
+  inline bool closure (hb_closure_context_t *c, ContextClosureLookupContext &lookup_context) const
+  {
+    TRACE_CLOSURE ();
+    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].static_size * (inputCount ? inputCount - 1 : 0));
+    return context_closure_lookup (c,
+				   inputCount, input,
+				   lookupCount, lookupRecord,
+				   lookup_context);
+  }
+
+  inline bool apply (hb_apply_context_t *c, ContextApplyLookupContext &lookup_context) const
   {
     TRACE_APPLY ();
     const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].static_size * (inputCount ? inputCount - 1 : 0));
-    return context_lookup (c,
-			   inputCount, input,
-			   lookupCount, lookupRecord,
-			   lookup_context);
+    return context_apply_lookup (c,
+				 inputCount, input,
+				 lookupCount, lookupRecord,
+				 lookup_context);
   }
 
   public:
@@ -459,7 +532,7 @@ struct Rule
 
   private:
   USHORT	inputCount;		/* Total number of glyphs in input
-					 * glyph sequence--includes the  first
+					 * glyph sequence--includes the first
 					 * glyph */
   USHORT	lookupCount;		/* Number of LookupRecords */
   USHORT	input[VAR];		/* Array of match inputs--start with
@@ -472,7 +545,17 @@ struct Rule
 
 struct RuleSet
 {
-  inline bool apply (hb_apply_context_t *c, ContextLookupContext &lookup_context) const
+  inline bool closure (hb_closure_context_t *c, ContextClosureLookupContext &lookup_context) const
+  {
+    TRACE_CLOSURE ();
+    bool ret = false;
+    unsigned int num_rules = rule.len;
+    for (unsigned int i = 0; i < num_rules; i++)
+      ret = (this+rule[i]).closure (c, lookup_context) || ret;
+    return ret;
+  }
+
+  inline bool apply (hb_apply_context_t *c, ContextApplyLookupContext &lookup_context) const
   {
     TRACE_APPLY ();
     unsigned int num_rules = rule.len;
@@ -481,7 +564,6 @@ struct RuleSet
       if ((this+rule[i]).apply (c, lookup_context))
         return true;
     }
-
     return false;
   }
 
@@ -508,8 +590,22 @@ struct ContextFormat1
   inline bool closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
   {
     TRACE_CLOSURE ();
-    /* TODO FILLME */
-    return false;
+
+    const Coverage &cov = (this+coverage);
+
+    struct ContextClosureLookupContext lookup_context = {
+      {intersects_glyph, closure_func},
+      NULL
+    };
+
+    bool ret = false;
+    unsigned int count = ruleSet.len;
+    for (unsigned int i = 0; i < count; i++)
+      if (cov.intersects_coverage (c->glyphs, i)) {
+	const RuleSet &rule_set = this+ruleSet[i];
+	ret = rule_set.closure (c, lookup_context) || ret;
+      }
+    return ret;
   }
 
   inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
@@ -520,7 +616,7 @@ struct ContextFormat1
       return false;
 
     const RuleSet &rule_set = this+ruleSet[index];
-    struct ContextLookupContext lookup_context = {
+    struct ContextApplyLookupContext lookup_context = {
       {match_glyph, apply_func},
       NULL
     };
@@ -555,8 +651,24 @@ struct ContextFormat2
   inline bool closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
   {
     TRACE_CLOSURE ();
-    /* TODO FILLME */
-    return false;
+    if (!(this+coverage).intersects (c->glyphs))
+      return false;
+
+    const ClassDef &class_def = this+classDef;
+
+    struct ContextClosureLookupContext lookup_context = {
+      {intersects_class, closure_func},
+      NULL
+    };
+
+    bool ret = false;
+    unsigned int count = ruleSet.len;
+    for (unsigned int i = 0; i < count; i++)
+      if (class_def.intersects_class (c->glyphs, i)) {
+	const RuleSet &rule_set = this+ruleSet[i];
+	ret = rule_set.closure (c, lookup_context) || ret;
+      }
+    return ret;
   }
 
   inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
@@ -569,7 +681,7 @@ struct ContextFormat2
     const ClassDef &class_def = this+classDef;
     index = class_def (c->buffer->info[c->buffer->idx].codepoint);
     const RuleSet &rule_set = this+ruleSet[index];
-    struct ContextLookupContext lookup_context = {
+    struct ContextApplyLookupContext lookup_context = {
       {match_class, apply_func},
       &class_def
     };
@@ -608,8 +720,18 @@ struct ContextFormat3
   inline bool closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
   {
     TRACE_CLOSURE ();
-    /* TODO FILLME */
-    return false;
+    if (!(this+coverage[0]).intersects (c->glyphs))
+      return false;
+
+    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * glyphCount);
+    struct ContextClosureLookupContext lookup_context = {
+      {intersects_coverage, closure_func},
+      this
+    };
+    return context_closure_lookup (c,
+				   glyphCount, (const USHORT *) (coverage + 1),
+				   lookupCount, lookupRecord,
+				   lookup_context);
   }
 
   inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
@@ -620,14 +742,14 @@ struct ContextFormat3
       return false;
 
     const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * glyphCount);
-    struct ContextLookupContext lookup_context = {
+    struct ContextApplyLookupContext lookup_context = {
       {match_coverage, apply_func},
       this
     };
-    return context_lookup (c,
-			   glyphCount, (const USHORT *) (coverage + 1),
-			   lookupCount, lookupRecord,
-			   lookup_context);
+    return context_apply_lookup (c,
+				 glyphCount, (const USHORT *) (coverage + 1),
+				 lookupCount, lookupRecord,
+				 lookup_context);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
@@ -704,22 +826,53 @@ struct Context
 
 /* Chaining Contextual lookups */
 
-struct ChainContextLookupContext
+struct ChainContextClosureLookupContext
 {
-  ContextFuncs funcs;
+  ContextClosureFuncs funcs;
+  const void *intersects_data[3];
+};
+
+struct ChainContextApplyLookupContext
+{
+  ContextApplyFuncs funcs;
   const void *match_data[3];
 };
 
-static inline bool chain_context_lookup (hb_apply_context_t *c,
-					 unsigned int backtrackCount,
-					 const USHORT backtrack[],
-					 unsigned int inputCount, /* Including the first glyph (not matched) */
-					 const USHORT input[], /* Array of input values--start with second glyph */
-					 unsigned int lookaheadCount,
-					 const USHORT lookahead[],
-					 unsigned int lookupCount,
-					 const LookupRecord lookupRecord[],
-					 ChainContextLookupContext &lookup_context)
+static inline bool chain_context_closure_lookup (hb_closure_context_t *c,
+						 unsigned int backtrackCount,
+						 const USHORT backtrack[],
+						 unsigned int inputCount, /* Including the first glyph (not matched) */
+						 const USHORT input[], /* Array of input values--start with second glyph */
+						 unsigned int lookaheadCount,
+						 const USHORT lookahead[],
+						 unsigned int lookupCount,
+						 const LookupRecord lookupRecord[],
+						 ChainContextClosureLookupContext &lookup_context)
+{
+  return intersects_array (c,
+			   backtrackCount, backtrack,
+			   lookup_context.funcs.intersects, lookup_context.intersects_data[0])
+      && intersects_array (c,
+			   inputCount ? inputCount - 1 : 0, input,
+			   lookup_context.funcs.intersects, lookup_context.intersects_data[1])
+      && intersects_array (c,
+			   lookaheadCount, lookahead,
+			   lookup_context.funcs.intersects, lookup_context.intersects_data[2])
+      && closure_lookup (c,
+			 lookupCount, lookupRecord,
+			 lookup_context.funcs.closure);
+}
+
+static inline bool chain_context_apply_lookup (hb_apply_context_t *c,
+					       unsigned int backtrackCount,
+					       const USHORT backtrack[],
+					       unsigned int inputCount, /* Including the first glyph (not matched) */
+					       const USHORT input[], /* Array of input values--start with second glyph */
+					       unsigned int lookaheadCount,
+					       const USHORT lookahead[],
+					       unsigned int lookupCount,
+					       const LookupRecord lookupRecord[],
+					       ChainContextApplyLookupContext &lookup_context)
 {
   /* First guess */
   if (unlikely (c->buffer->backtrack_len () < backtrackCount ||
@@ -750,18 +903,33 @@ struct ChainRule
   friend struct ChainRuleSet;
 
   private:
-  inline bool apply (hb_apply_context_t *c, ChainContextLookupContext &lookup_context) const
+
+  inline bool closure (hb_closure_context_t *c, ChainContextClosureLookupContext &lookup_context) const
+  {
+    TRACE_CLOSURE ();
+    const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
+    const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
+    const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
+    return chain_context_closure_lookup (c,
+					 backtrack.len, backtrack.array,
+					 input.len, input.array,
+					 lookahead.len, lookahead.array,
+					 lookup.len, lookup.array,
+					 lookup_context);
+  }
+
+  inline bool apply (hb_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
   {
     TRACE_APPLY ();
     const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
     const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
     const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
-    return chain_context_lookup (c,
-				 backtrack.len, backtrack.array,
-				 input.len, input.array,
-				 lookahead.len, lookahead.array,
-				 lookup.len, lookup.array,
-				 lookup_context);
+    return chain_context_apply_lookup (c,
+				       backtrack.len, backtrack.array,
+				       input.len, input.array,
+				       lookahead.len, lookahead.array,
+				       lookup.len, lookup.array,
+				       lookup_context);
   }
 
   public:
@@ -796,7 +964,17 @@ struct ChainRule
 
 struct ChainRuleSet
 {
-  inline bool apply (hb_apply_context_t *c, ChainContextLookupContext &lookup_context) const
+  inline bool closure (hb_closure_context_t *c, ChainContextClosureLookupContext &lookup_context) const
+  {
+    TRACE_CLOSURE ();
+    bool ret = false;
+    unsigned int num_rules = rule.len;
+    for (unsigned int i = 0; i < num_rules; i++)
+      ret = (this+rule[i]).closure (c, lookup_context) || ret;
+    return ret;
+  }
+
+  inline bool apply (hb_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
   {
     TRACE_APPLY ();
     unsigned int num_rules = rule.len;
@@ -831,8 +1009,21 @@ struct ChainContextFormat1
   inline bool closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
   {
     TRACE_CLOSURE ();
-    /* TODO FILLME */
-    return false;
+    const Coverage &cov = (this+coverage);
+
+    struct ChainContextClosureLookupContext lookup_context = {
+      {intersects_glyph, closure_func},
+      {NULL, NULL, NULL}
+    };
+
+    bool ret = false;
+    unsigned int count = ruleSet.len;
+    for (unsigned int i = 0; i < count; i++)
+      if (cov.intersects_coverage (c->glyphs, i)) {
+	const ChainRuleSet &rule_set = this+ruleSet[i];
+	ret = rule_set.closure (c, lookup_context) || ret;
+      }
+    return ret;
   }
 
   inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
@@ -843,7 +1034,7 @@ struct ChainContextFormat1
       return false;
 
     const ChainRuleSet &rule_set = this+ruleSet[index];
-    struct ChainContextLookupContext lookup_context = {
+    struct ChainContextApplyLookupContext lookup_context = {
       {match_glyph, apply_func},
       {NULL, NULL, NULL}
     };
@@ -877,8 +1068,28 @@ struct ChainContextFormat2
   inline bool closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
   {
     TRACE_CLOSURE ();
-    /* TODO FILLME */
-    return false;
+    if (!(this+coverage).intersects (c->glyphs))
+      return false;
+
+    const ClassDef &backtrack_class_def = this+backtrackClassDef;
+    const ClassDef &input_class_def = this+inputClassDef;
+    const ClassDef &lookahead_class_def = this+lookaheadClassDef;
+
+    struct ChainContextClosureLookupContext lookup_context = {
+      {intersects_class, closure_func},
+      {&backtrack_class_def,
+       &input_class_def,
+       &lookahead_class_def}
+    };
+
+    bool ret = false;
+    unsigned int count = ruleSet.len;
+    for (unsigned int i = 0; i < count; i++)
+      if (input_class_def.intersects_class (c->glyphs, i)) {
+	const ChainRuleSet &rule_set = this+ruleSet[i];
+	ret = rule_set.closure (c, lookup_context) || ret;
+      }
+    return ret;
   }
 
   inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
@@ -894,7 +1105,7 @@ struct ChainContextFormat2
 
     index = input_class_def (c->buffer->info[c->buffer->idx].codepoint);
     const ChainRuleSet &rule_set = this+ruleSet[index];
-    struct ChainContextLookupContext lookup_context = {
+    struct ChainContextApplyLookupContext lookup_context = {
       {match_class, apply_func},
       {&backtrack_class_def,
        &input_class_def,
@@ -960,16 +1171,16 @@ struct ChainContextFormat3
 
     const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
     const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
-    struct ChainContextLookupContext lookup_context = {
+    struct ChainContextApplyLookupContext lookup_context = {
       {match_coverage, apply_func},
       {this, this, this}
     };
-    return chain_context_lookup (c,
-				 backtrack.len, (const USHORT *) backtrack.array,
-				 input.len, (const USHORT *) input.array + 1,
-				 lookahead.len, (const USHORT *) lookahead.array,
-				 lookup.len, lookup.array,
-				 lookup_context);
+    return chain_context_apply_lookup (c,
+				       backtrack.len, (const USHORT *) backtrack.array,
+				       input.len, (const USHORT *) input.array + 1,
+				       lookahead.len, (const USHORT *) lookahead.array,
+				       lookup.len, lookup.array,
+				       lookup_context);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
diff --git a/src/hb-ot-layout-private.hh b/src/hb-ot-layout-private.hh
index acc1d18..92f3c26 100644
--- a/src/hb-ot-layout-private.hh
+++ b/src/hb-ot-layout-private.hh
@@ -100,11 +100,8 @@ struct _hb_glyph_map_t
   void clear (void) {
     memset (elts, 0, sizeof elts);
   }
-  bool has (hb_codepoint_t g) const {
-    if (unlikely (g > MAX_G)) return false;
-    return !!(elt (g) & mask (g));
-  }
-  bool add (hb_codepoint_t g) {
+  bool add (hb_codepoint_t g)
+  {
     if (unlikely (g > MAX_G)) return false;
     elt_t &e = elt (g);
     elt_t m = mask (g);
@@ -112,6 +109,22 @@ struct _hb_glyph_map_t
     e |= m;
     return ret;
   }
+  bool has (hb_codepoint_t g) const
+  {
+    if (unlikely (g > MAX_G)) return false;
+    return !!(elt (g) & mask (g));
+  }
+  bool intersects (hb_codepoint_t first,
+		   hb_codepoint_t last) const
+  {
+    if (unlikely (first > MAX_G)) return false;
+    if (unlikely (last  > MAX_G)) last = MAX_G;
+    unsigned int end = last + 1;
+    for (hb_codepoint_t i = first; i < end; i++)
+      if (has (i))
+        return true;
+    return false;
+  }
 
   private:
   typedef uint32_t elt_t;
commit c64ddab3c34897cd520d4d73a054866e649e8793
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Mon Apr 23 15:28:35 2012 -0400

    Flesh out closure() for GSUB
    
    The GSUBGPOS part still missing.

diff --git a/src/hb-ot-layout-common-private.hh b/src/hb-ot-layout-common-private.hh
index c41010a..410c933 100644
--- a/src/hb-ot-layout-common-private.hh
+++ b/src/hb-ot-layout-common-private.hh
@@ -355,10 +355,11 @@ struct CoverageFormat1
   }
 
   struct Iter {
-    void init (const struct CoverageFormat1 &c_) { c = &c_; i = 0; };
-    bool more (void) { return i < c->glyphArray.len; }
-    void next (void) { i++; }
-    uint16_t get (void) { return c->glyphArray[i]; }
+    inline void init (const struct CoverageFormat1 &c_) { c = &c_; i = 0; };
+    inline bool more (void) { return i < c->glyphArray.len; }
+    inline void next (void) { i++; }
+    inline uint16_t get_glyph (void) { return c->glyphArray[i]; }
+    inline uint16_t get_coverage (void) { return i; }
 
     private:
     const struct CoverageFormat1 *c;
@@ -394,9 +395,15 @@ struct CoverageFormat2
   }
 
   struct Iter {
-    void init (const CoverageFormat2 &c_) { c = &c_; i = 0; j = c->rangeRecord.len ? c_.rangeRecord[0].start : 0; }
-    bool more (void) { return i < c->rangeRecord.len; }
-    void next (void) {
+    inline void init (const CoverageFormat2 &c_) {
+      c = &c_;
+      coverage = 0;
+      i = 0;
+      j = c->rangeRecord.len ? c_.rangeRecord[0].start : 0;
+    }
+    inline bool more (void) { return i < c->rangeRecord.len; }
+    inline void next (void) {
+      coverage++;
       if (j == c->rangeRecord[i].end) {
         i++;
 	if (more ())
@@ -405,13 +412,12 @@ struct CoverageFormat2
       }
       j++;
     }
-    uint16_t get (void) {
-      return j;
-    }
+    inline uint16_t get_glyph (void) { return j; }
+    inline uint16_t get_coverage (void) { return coverage; }
 
     private:
     const struct CoverageFormat2 *c;
-    unsigned int i, j;
+    unsigned int i, j, coverage;
   };
 
   private:
@@ -447,8 +453,20 @@ struct Coverage
     }
   }
 
+  inline bool intersects (const hb_glyph_map_t *glyphs) const {
+    /* TODO speed this up */
+    Coverage::Iter iter;
+    for (iter.init (*this); iter.more (); iter.next ()) {
+      if (glyphs->has (iter.get_glyph ()))
+        return true;
+    }
+    return false;
+
+  }
+
   struct Iter {
-    void init (const Coverage &c_) {
+    Iter (void) : format (0) {};
+    inline void init (const Coverage &c_) {
       format = c_.u.format;
       switch (format) {
       case 1: return u.format1.init (c_.u.format1);
@@ -456,30 +474,37 @@ struct Coverage
       default:return;
       }
     }
-    bool more (void) {
+    inline bool more (void) {
       switch (format) {
       case 1: return u.format1.more ();
       case 2: return u.format2.more ();
       default:return true;
       }
     }
-    void next (void) {
+    inline void next (void) {
       switch (format) {
       case 1: u.format1.next (); break;
       case 2: u.format2.next (); break;
       default:                   break;
       }
     }
-    uint16_t get (void) {
+    inline uint16_t get_glyph (void) {
+      switch (format) {
+      case 1: return u.format1.get_glyph ();
+      case 2: return u.format2.get_glyph ();
+      default:return true;
+      }
+    }
+    inline uint16_t get_coverage (void) {
       switch (format) {
-      case 1: return u.format1.get ();
-      case 2: return u.format2.get ();
+      case 1: return u.format1.get_coverage ();
+      case 2: return u.format2.get_coverage ();
       default:return true;
       }
     }
 
     private:
-    USHORT format;
+    unsigned int format;
     union {
     CoverageFormat1::Iter	format1;
     CoverageFormat2::Iter	format2;
diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh
index f0db228..1feb3c8 100644
--- a/src/hb-ot-layout-gsub-table.hh
+++ b/src/hb-ot-layout-gsub-table.hh
@@ -42,8 +42,14 @@ struct SingleSubstFormat1
   inline bool closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE ();
-    /* TODO FILLME */
-    return false;
+    bool ret = false;
+    Coverage::Iter iter;
+    for (iter.init (this+coverage); iter.more (); iter.next ()) {
+      hb_codepoint_t glyph_id = iter.get_glyph ();
+      if (c->glyphs->has (glyph_id))
+	ret = c->glyphs->add ((glyph_id + deltaGlyphID) & 0xFFFF) || ret;
+    }
+    return ret;
   }
 
   inline bool would_apply (hb_codepoint_t glyph_id) const
@@ -93,8 +99,13 @@ struct SingleSubstFormat2
   inline bool closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE ();
-    /* TODO FILLME */
-    return false;
+    bool ret = false;
+    Coverage::Iter iter;
+    for (iter.init (this+coverage); iter.more (); iter.next ()) {
+      if (c->glyphs->has (iter.get_glyph ()))
+	ret = c->glyphs->add (substitute[iter.get_coverage ()]) || ret;
+    }
+    return ret;
   }
 
   inline bool would_apply (hb_codepoint_t glyph_id) const
@@ -200,8 +211,11 @@ struct Sequence
   inline bool closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE ();
-    /* TODO FILLME */
-    return false;
+    unsigned int count = substitute.len;
+    bool ret = false;
+    for (unsigned int i = 0; i < count; i++)
+      ret = c->glyphs->add (substitute[i]) || ret;
+    return ret;
   }
 
   inline bool apply (hb_apply_context_t *c) const
@@ -239,8 +253,13 @@ struct MultipleSubstFormat1
   inline bool closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE ();
-    /* TODO FILLME */
-    return false;
+    bool ret = false;
+    Coverage::Iter iter;
+    for (iter.init (this+coverage); iter.more (); iter.next ()) {
+      if (c->glyphs->has (iter.get_glyph ()))
+	ret = (this+sequence[iter.get_coverage ()]).closure (c) || ret;
+    }
+    return ret;
   }
 
   inline bool would_apply (hb_codepoint_t glyph_id) const
@@ -338,8 +357,17 @@ struct AlternateSubstFormat1
   inline bool closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE ();
-    /* TODO FILLME */
-    return false;
+    bool ret = false;
+    Coverage::Iter iter;
+    for (iter.init (this+coverage); iter.more (); iter.next ()) {
+      if (c->glyphs->has (iter.get_glyph ())) {
+	const AlternateSet &alt_set = this+alternateSet[iter.get_coverage ()];
+	unsigned int count = alt_set.len;
+	for (unsigned int i = 0; i < count; i++)
+	  ret = c->glyphs->add (alt_set[i]) || ret;
+      }
+    }
+    return ret;
   }
 
   inline bool would_apply (hb_codepoint_t glyph_id) const
@@ -454,8 +482,11 @@ struct Ligature
   inline bool closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE ();
-    /* TODO FILLME */
-    return false;
+    unsigned int count = component.len;
+    for (unsigned int i = 1; i < count; i++)
+      if (!c->glyphs->has (component[i]))
+        return false;
+    return c->glyphs->add (ligGlyph);
   }
 
   inline bool would_apply (hb_codepoint_t second) const
@@ -556,8 +587,14 @@ struct LigatureSet
   inline bool closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE ();
-    /* TODO FILLME */
-    return false;
+    bool ret = false;
+    unsigned int num_ligs = ligature.len;
+    for (unsigned int i = 0; i < num_ligs; i++)
+    {
+      const Ligature &lig = this+ligature[i];
+      ret = lig.closure (c) || ret;
+    }
+    return ret;
   }
 
   inline bool would_apply (hb_codepoint_t second) const
@@ -609,7 +646,13 @@ struct LigatureSubstFormat1
   inline bool closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE ();
-    /* TODO FILLME */
+    bool ret = false;
+    Coverage::Iter iter;
+    for (iter.init (this+coverage); iter.more (); iter.next ()) {
+      if (c->glyphs->has (iter.get_glyph ()))
+	ret = (this+ligatureSet[iter.get_coverage ()]).closure (c) || ret;
+    }
+    return ret;
     return false;
   }
 
@@ -755,13 +798,7 @@ struct ExtensionSubst : Extension
     return StructAtOffset<SubstLookupSubTable> (this, offset);
   }
 
-  inline bool closure (hb_closure_context_t *c) const
-  {
-    TRACE_CLOSURE ();
-    /* TODO FILLME */
-    return false;
-  }
-
+  inline bool closure (hb_closure_context_t *c) const;
   inline bool would_apply (hb_codepoint_t glyph_id) const;
   inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const;
 
@@ -782,7 +819,27 @@ struct ReverseChainSingleSubstFormat1
   inline bool closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE ();
-    /* TODO FILLME */
+    const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
+
+    unsigned int count;
+
+    count = backtrack.len;
+    for (unsigned int i = 0; i < count; i++)
+      if (!(this+backtrack[i]).intersects (c->glyphs))
+        return false;
+
+    count = lookahead.len;
+    for (unsigned int i = 0; i < count; i++)
+      if (!(this+lookahead[i]).intersects (c->glyphs))
+        return false;
+
+    const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
+    bool ret = false;
+    Coverage::Iter iter;
+    for (iter.init (this+coverage); iter.more (); iter.next ()) {
+      if (c->glyphs->has (iter.get_glyph ()))
+	ret = c->glyphs->add (substitute[iter.get_coverage ()]) || ret;
+    }
     return false;
   }
 
@@ -1014,8 +1071,8 @@ struct SubstLookup : Lookup
   inline bool closure (hb_closure_context_t *c) const
   {
     unsigned int lookup_type = get_type ();
-    unsigned int count = get_subtable_count ();
     bool ret = false;
+    unsigned int count = get_subtable_count ();
     for (unsigned int i = 0; i < count; i++)
       ret = get_subtable (i).closure (c, lookup_type) || ret;
     return ret;
@@ -1054,8 +1111,8 @@ struct SubstLookup : Lookup
        *
        * This is rather slow to do this here for every glyph,
        * but it's easiest, and who uses extension lookups anyway?!*/
-      unsigned int count = get_subtable_count ();
       unsigned int type = get_subtable(0).u.extension.get_type ();
+      unsigned int count = get_subtable_count ();
       for (unsigned int i = 1; i < count; i++)
         if (get_subtable(i).u.extension.get_type () != type)
 	  return false;
@@ -1174,6 +1231,11 @@ GSUB::substitute_finish (hb_buffer_t *buffer)
 
 /* Out-of-class implementation for methods recursing */
 
+inline bool ExtensionSubst::closure (hb_closure_context_t *c) const
+{
+  return get_subtable ().closure (c, get_type ());
+}
+
 inline bool ExtensionSubst::would_apply (hb_codepoint_t glyph_id) const
 {
   return get_subtable ().would_apply (glyph_id, get_type ());
diff --git a/src/hb-ot-layout-private.hh b/src/hb-ot-layout-private.hh
index bf7e43b..acc1d18 100644
--- a/src/hb-ot-layout-private.hh
+++ b/src/hb-ot-layout-private.hh
@@ -95,4 +95,38 @@ _hb_ot_layout_destroy (hb_ot_layout_t *layout);
 
 
 
+struct _hb_glyph_map_t
+{
+  void clear (void) {
+    memset (elts, 0, sizeof elts);
+  }
+  bool has (hb_codepoint_t g) const {
+    if (unlikely (g > MAX_G)) return false;
+    return !!(elt (g) & mask (g));
+  }
+  bool add (hb_codepoint_t g) {
+    if (unlikely (g > MAX_G)) return false;
+    elt_t &e = elt (g);
+    elt_t m = mask (g);
+    bool ret = !!(e & m);
+    e |= m;
+    return ret;
+  }
+
+  private:
+  typedef uint32_t elt_t;
+  static const unsigned int MAX_G = 65536 - 1;
+  static const unsigned int SHIFT = 5;
+  static const unsigned int BITS = (1 << SHIFT);
+  static const unsigned int MASK = BITS - 1;
+
+  elt_t &elt (hb_codepoint_t g) { return elts[g >> SHIFT]; }
+  elt_t elt (hb_codepoint_t g) const { return elts[g >> SHIFT]; }
+  elt_t mask (hb_codepoint_t g) const { return elt_t (1) << (g & MASK); }
+
+  elt_t elts[(MAX_G + 1 + (BITS - 1)) / BITS]; /* 8kb */
+  ASSERT_STATIC (sizeof (elt_t) * 8 == BITS);
+  ASSERT_STATIC (sizeof (elts) * 8 > MAX_G);
+};
+
 #endif /* HB_OT_LAYOUT_PRIVATE_HH */
diff --git a/src/hb-ot-layout.h b/src/hb-ot-layout.h
index 16addc6..a9db04f 100644
--- a/src/hb-ot-layout.h
+++ b/src/hb-ot-layout.h
@@ -183,7 +183,7 @@ void
 hb_ot_layout_substitute_finish (hb_buffer_t  *buffer);
 
 
-typedef uint32_t hb_glyph_map_t[65536 / 32]; /* 8kb */
+typedef struct _hb_glyph_map_t hb_glyph_map_t;
 
 hb_bool_t
 hb_ot_layout_substitute_closure_lookup (hb_face_t      *face,
commit 0da132bde4d576a03095d6738507954f7f85103d
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Mon Apr 23 14:21:33 2012 -0400

    Fix Coverage iters

diff --git a/src/hb-ot-layout-common-private.hh b/src/hb-ot-layout-common-private.hh
index 48840f2..c41010a 100644
--- a/src/hb-ot-layout-common-private.hh
+++ b/src/hb-ot-layout-common-private.hh
@@ -357,7 +357,8 @@ struct CoverageFormat1
   struct Iter {
     void init (const struct CoverageFormat1 &c_) { c = &c_; i = 0; };
     bool more (void) { return i < c->glyphArray.len; }
-    uint16_t next (void) { return c->glyphArray[i++]; }
+    void next (void) { i++; }
+    uint16_t get (void) { return c->glyphArray[i]; }
 
     private:
     const struct CoverageFormat1 *c;
@@ -394,14 +395,18 @@ struct CoverageFormat2
 
   struct Iter {
     void init (const CoverageFormat2 &c_) { c = &c_; i = 0; j = c->rangeRecord.len ? c_.rangeRecord[0].start : 0; }
-    bool more (void) { return i < c->rangeRecord.len ||
-			      (i == c->rangeRecord.len && j < c->rangeRecord[i].end); }
-    uint16_t next (void) {
+    bool more (void) { return i < c->rangeRecord.len; }
+    void next (void) {
       if (j == c->rangeRecord[i].end) {
         i++;
-	j = c->rangeRecord[i].start;
+	if (more ())
+	  j = c->rangeRecord[i].start;
+	return;
       }
-      return j++;
+      j++;
+    }
+    uint16_t get (void) {
+      return j;
     }
 
     private:
@@ -458,10 +463,17 @@ struct Coverage
       default:return true;
       }
     }
-    uint16_t next (void) {
+    void next (void) {
+      switch (format) {
+      case 1: u.format1.next (); break;
+      case 2: u.format2.next (); break;
+      default:                   break;
+      }
+    }
+    uint16_t get (void) {
       switch (format) {
-      case 1: return u.format1.next ();
-      case 2: return u.format2.next ();
+      case 1: return u.format1.get ();
+      case 2: return u.format2.get ();
       default:return true;
       }
     }
commit 3e32cd9570fd8b09901fb790b80365ae425f681a
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Mon Apr 23 13:20:52 2012 -0400

    Minor

diff --git a/src/hb-ot-layout-gsubgpos-private.hh b/src/hb-ot-layout-gsubgpos-private.hh
index 810a495..46e322b 100644
--- a/src/hb-ot-layout-gsubgpos-private.hh
+++ b/src/hb-ot-layout-gsubgpos-private.hh
@@ -31,7 +31,6 @@
 
 #include "hb-buffer-private.hh"
 #include "hb-ot-layout-gdef-table.hh"
-#include "hb-ot-layout-closure.h"
 
 
 
diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index b018661..4441a7e 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -471,9 +471,9 @@ hb_ot_layout_substitute_finish (hb_buffer_t  *buffer HB_UNUSED)
 }
 
 hb_bool_t
-hb_ot_layout_closure_lookup (hb_face_t      *face,
-			     hb_glyph_map_t *glyphs,
-			     unsigned int    lookup_index)
+hb_ot_layout_substitute_closure_lookup (hb_face_t      *face,
+				        hb_glyph_map_t *glyphs,
+				        unsigned int    lookup_index)
 {
   hb_closure_context_t c (face, glyphs);
   return _get_gsub (face).closure_lookup (&c, lookup_index);
diff --git a/src/hb-ot-layout.h b/src/hb-ot-layout.h
index 430e54c..16addc6 100644
--- a/src/hb-ot-layout.h
+++ b/src/hb-ot-layout.h
@@ -182,6 +182,14 @@ hb_ot_layout_substitute_lookup (hb_face_t    *face,
 void
 hb_ot_layout_substitute_finish (hb_buffer_t  *buffer);
 
+
+typedef uint32_t hb_glyph_map_t[65536 / 32]; /* 8kb */
+
+hb_bool_t
+hb_ot_layout_substitute_closure_lookup (hb_face_t      *face,
+				        hb_glyph_map_t *glyphs,
+				        unsigned int    lookup_index);
+
 /*
  * GPOS
  */
diff --git a/src/hb-private.hh b/src/hb-private.hh
index 64cb3c8..4f211a1 100644
--- a/src/hb-private.hh
+++ b/src/hb-private.hh
@@ -34,9 +34,11 @@
 #endif
 
 #include "hb.h"
-#include "hb-ot.h"
 #define HB_H_IN
+#ifdef HAVE_OT
+#include "hb-ot.h"
 #define HB_OT_H_IN
+#endif
 
 #include <stdlib.h>
 #include <stddef.h>
commit 650ac00da3d2f988197393f34d40f0ba1a0fa093
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Mon Apr 23 13:17:09 2012 -0400

    Minor refactoring

diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh
index 7a3c117..6e5b85e 100644
--- a/src/hb-ot-layout-gpos-table.hh
+++ b/src/hb-ot-layout-gpos-table.hh
@@ -1417,24 +1417,22 @@ struct PosLookup : Lookup
     return false;
   }
 
-   inline bool apply_string (hb_font_t   *font,
-			     hb_buffer_t *buffer,
-			     hb_mask_t    mask) const
+  inline bool apply_string (hb_apply_context_t *c) const
   {
     bool ret = false;
 
-    if (unlikely (!buffer->len))
+    if (unlikely (!c->buffer->len))
       return false;
 
-    hb_apply_context_t c (font, font->face, buffer, mask, *this);
+    c->set_lookup (*this);
 
-    buffer->idx = 0;
-    while (buffer->idx < buffer->len)
+    c->buffer->idx = 0;
+    while (c->buffer->idx < c->buffer->len)
     {
-      if ((buffer->info[buffer->idx].mask & mask) && apply_once (&c))
+      if ((c->buffer->info[c->buffer->idx].mask & c->lookup_mask) && apply_once (c))
 	ret = true;
       else
-	buffer->idx++;
+	c->buffer->idx++;
     }
 
     return ret;
@@ -1461,11 +1459,8 @@ struct GPOS : GSUBGPOS
   inline const PosLookup& get_lookup (unsigned int i) const
   { return CastR<PosLookup> (GSUBGPOS::get_lookup (i)); }
 
-  inline bool position_lookup (hb_font_t    *font,
-			       hb_buffer_t  *buffer,
-			       unsigned int  lookup_index,
-			       hb_mask_t     mask) const
-  { return get_lookup (lookup_index).apply_string (font, buffer, mask); }
+  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_buffer_t *buffer);
   static inline void position_finish (hb_buffer_t *buffer);
@@ -1584,7 +1579,9 @@ static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_i
   if (unlikely (c->context_length < 1))
     return false;
 
-  hb_apply_context_t new_c (*c, l);
+  hb_apply_context_t new_c (*c);
+  new_c.nesting_level_left--;
+  new_c.set_lookup (l);
   return l.apply_once (&new_c);
 }
 
diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh
index 54d81e5..f0db228 100644
--- a/src/hb-ot-layout-gsub-table.hh
+++ b/src/hb-ot-layout-gsub-table.hh
@@ -1069,46 +1069,44 @@ struct SubstLookup : Lookup
     return false;
   }
 
-  inline bool apply_string (hb_face_t   *face,
-			    hb_buffer_t *buffer,
-			    hb_mask_t    mask) const
+  inline bool apply_string (hb_apply_context_t *c) const
   {
     bool ret = false;
 
-    if (unlikely (!buffer->len))
+    if (unlikely (!c->buffer->len))
       return false;
 
-    hb_apply_context_t c (NULL, face, buffer, mask, *this);
+    c->set_lookup (*this);
 
     if (likely (!is_reverse ()))
     {
 	/* in/out forward substitution */
-	buffer->clear_output ();
-	buffer->idx = 0;
-	while (buffer->idx < buffer->len)
+	c->buffer->clear_output ();
+	c->buffer->idx = 0;
+	while (c->buffer->idx < c->buffer->len)
 	{
-	  if ((buffer->info[buffer->idx].mask & mask) && apply_once (&c))
+	  if ((c->buffer->info[c->buffer->idx].mask & c->lookup_mask) && apply_once (c))
 	    ret = true;
 	  else
-	    buffer->next_glyph ();
+	    c->buffer->next_glyph ();
 
 	}
 	if (ret)
-	  buffer->swap_buffers ();
+	  c->buffer->swap_buffers ();
     }
     else
     {
 	/* in-place backward substitution */
-	buffer->idx = buffer->len - 1;
+	c->buffer->idx = c->buffer->len - 1;
 	do
 	{
-	  if ((buffer->info[buffer->idx].mask & mask) && apply_once (&c))
+	  if ((c->buffer->info[c->buffer->idx].mask & c->lookup_mask) && apply_once (c))
 	    ret = true;
 	  else
-	    buffer->idx--;
+	    c->buffer->idx--;
 
 	}
-	while ((int) buffer->idx >= 0);
+	while ((int) c->buffer->idx >= 0);
     }
 
     return ret;
@@ -1135,11 +1133,8 @@ struct GSUB : GSUBGPOS
   inline const SubstLookup& get_lookup (unsigned int i) const
   { return CastR<SubstLookup> (GSUBGPOS::get_lookup (i)); }
 
-  inline bool substitute_lookup (hb_face_t    *face,
-				 hb_buffer_t  *buffer,
-			         unsigned int  lookup_index,
-				 hb_mask_t     mask) const
-  { return get_lookup (lookup_index).apply_string (face, buffer, mask); }
+  inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index) const
+  { return get_lookup (lookup_index).apply_string (c); }
 
   static inline void substitute_start (hb_buffer_t *buffer);
   static inline void substitute_finish (hb_buffer_t *buffer);
@@ -1238,7 +1233,9 @@ static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup
   if (unlikely (c->context_length < 1))
     return false;
 
-  hb_apply_context_t new_c (*c, l);
+  hb_apply_context_t new_c (*c);
+  new_c.nesting_level_left--;
+  new_c.set_lookup (l);
   return l.apply_once (&new_c);
 }
 
diff --git a/src/hb-ot-layout-gsubgpos-private.hh b/src/hb-ot-layout-gsubgpos-private.hh
index d5bc377..810a495 100644
--- a/src/hb-ot-layout-gsubgpos-private.hh
+++ b/src/hb-ot-layout-gsubgpos-private.hh
@@ -103,7 +103,6 @@ struct hb_apply_context_t
 		      hb_face_t *face_,
 		      hb_buffer_t *buffer_,
 		      hb_mask_t lookup_mask_,
-		      const Lookup &l,
 		      unsigned int context_length_ = NO_CONTEXT,
 		      unsigned int nesting_level_left_ = MAX_NESTING_LEVEL) :
 			font (font_), face (face_), buffer (buffer_),
@@ -111,12 +110,9 @@ struct hb_apply_context_t
 			lookup_mask (lookup_mask_),
 			context_length (context_length_),
 			nesting_level_left (nesting_level_left_),
-			lookup_props (l.get_props ()),
-			property (0), debug_depth (0) {}
+			lookup_props (0), property (0), debug_depth (0) {}
 
-  hb_apply_context_t (const hb_apply_context_t &c, const Lookup &l) {
-    *this = c;
-    nesting_level_left--;
+  void set_lookup (const Lookup &l) {
     lookup_props = l.get_props ();
   }
 
diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index e515890..b018661 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -460,7 +460,8 @@ hb_ot_layout_substitute_lookup (hb_face_t    *face,
 				unsigned int  lookup_index,
 				hb_mask_t     mask)
 {
-  return _get_gsub (face).substitute_lookup (face, buffer, lookup_index, mask);
+  hb_apply_context_t c (NULL, face, buffer, mask);
+  return _get_gsub (face).substitute_lookup (&c, lookup_index);
 }
 
 void
@@ -500,7 +501,8 @@ hb_ot_layout_position_lookup   (hb_font_t    *font,
 				unsigned int  lookup_index,
 				hb_mask_t     mask)
 {
-  return _get_gpos (font->face).position_lookup (font, buffer, lookup_index, mask);
+  hb_apply_context_t c (font, font->face, buffer, mask);
+  return _get_gpos (font->face).position_lookup (&c, lookup_index);
 }
 
 void
commit f94b0aa64609654497ced9c00312c9643eb69053
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Mon Apr 23 13:04:38 2012 -0400

    Add "closure" operation stubs to GSUB
    
    Filling in.

diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh
index f2210a0..54d81e5 100644
--- a/src/hb-ot-layout-gsub-table.hh
+++ b/src/hb-ot-layout-gsub-table.hh
@@ -39,6 +39,13 @@ struct SingleSubstFormat1
 
   private:
 
+  inline bool closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    /* TODO FILLME */
+    return false;
+  }
+
   inline bool would_apply (hb_codepoint_t glyph_id) const
   {
     return (this+coverage) (glyph_id) != NOT_COVERED;
@@ -83,6 +90,13 @@ struct SingleSubstFormat2
 
   private:
 
+  inline bool closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    /* TODO FILLME */
+    return false;
+  }
+
   inline bool would_apply (hb_codepoint_t glyph_id) const
   {
     return (this+coverage) (glyph_id) != NOT_COVERED;
@@ -129,6 +143,16 @@ struct SingleSubst
 
   private:
 
+  inline bool closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    switch (u.format) {
+    case 1: return u.format1.closure (c);
+    case 2: return u.format2.closure (c);
+    default:return false;
+    }
+  }
+
   inline bool would_apply (hb_codepoint_t glyph_id) const
   {
     switch (u.format) {
@@ -172,6 +196,14 @@ struct Sequence
   friend struct MultipleSubstFormat1;
 
   private:
+
+  inline bool closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    /* TODO FILLME */
+    return false;
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
@@ -204,6 +236,13 @@ struct MultipleSubstFormat1
 
   private:
 
+  inline bool closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    /* TODO FILLME */
+    return false;
+  }
+
   inline bool would_apply (hb_codepoint_t glyph_id) const
   {
     return (this+coverage) (glyph_id) != NOT_COVERED;
@@ -244,6 +283,15 @@ struct MultipleSubst
 
   private:
 
+  inline bool closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    switch (u.format) {
+    case 1: return u.format1.closure (c);
+    default:return false;
+    }
+  }
+
   inline bool would_apply (hb_codepoint_t glyph_id) const
   {
     switch (u.format) {
@@ -287,6 +335,13 @@ struct AlternateSubstFormat1
 
   private:
 
+  inline bool closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    /* TODO FILLME */
+    return false;
+  }
+
   inline bool would_apply (hb_codepoint_t glyph_id) const
   {
     return (this+coverage) (glyph_id) != NOT_COVERED;
@@ -347,6 +402,15 @@ struct AlternateSubst
 
   private:
 
+  inline bool closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    switch (u.format) {
+    case 1: return u.format1.closure (c);
+    default:return false;
+    }
+  }
+
   inline bool would_apply (hb_codepoint_t glyph_id) const
   {
     switch (u.format) {
@@ -387,6 +451,13 @@ struct Ligature
 
   private:
 
+  inline bool closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    /* TODO FILLME */
+    return false;
+  }
+
   inline bool would_apply (hb_codepoint_t second) const
   {
     return component.len == 2 && component[1] == second;
@@ -482,6 +553,13 @@ struct LigatureSet
 
   private:
 
+  inline bool closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    /* TODO FILLME */
+    return false;
+  }
+
   inline bool would_apply (hb_codepoint_t second) const
   {
     unsigned int num_ligs = ligature.len;
@@ -528,6 +606,13 @@ struct LigatureSubstFormat1
 
   private:
 
+  inline bool closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    /* TODO FILLME */
+    return false;
+  }
+
   inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const
   {
     unsigned int index;
@@ -572,6 +657,15 @@ struct LigatureSubst
 
   private:
 
+  inline bool closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    switch (u.format) {
+    case 1: return u.format1.closure (c);
+    default:return false;
+    }
+  }
+
   inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const
   {
     switch (u.format) {
@@ -607,12 +701,20 @@ struct LigatureSubst
 
 
 static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index);
+static inline bool closure_lookup (hb_closure_context_t *c, unsigned int lookup_index);
 
 struct ContextSubst : Context
 {
   friend struct SubstLookupSubTable;
 
   private:
+
+  inline bool closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    return Context::closure (c, closure_lookup);
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
@@ -625,6 +727,13 @@ struct ChainContextSubst : ChainContext
   friend struct SubstLookupSubTable;
 
   private:
+
+  inline bool closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    return ChainContext::closure (c, closure_lookup);
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
@@ -646,6 +755,13 @@ struct ExtensionSubst : Extension
     return StructAtOffset<SubstLookupSubTable> (this, offset);
   }
 
+  inline bool closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    /* TODO FILLME */
+    return false;
+  }
+
   inline bool would_apply (hb_codepoint_t glyph_id) const;
   inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const;
 
@@ -662,6 +778,14 @@ struct ReverseChainSingleSubstFormat1
   friend struct ReverseChainSingleSubst;
 
   private:
+
+  inline bool closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    /* TODO FILLME */
+    return false;
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
@@ -728,6 +852,16 @@ struct ReverseChainSingleSubst
   friend struct SubstLookupSubTable;
 
   private:
+
+  inline bool closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    switch (u.format) {
+    case 1: return u.format1.closure (c);
+    default:return false;
+    }
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
@@ -774,6 +908,23 @@ struct SubstLookupSubTable
     ReverseChainSingle	= 8
   };
 
+  inline bool closure (hb_closure_context_t *c,
+		       unsigned int    lookup_type) const
+  {
+    TRACE_CLOSURE ();
+    switch (lookup_type) {
+    case Single:		return u.single.closure (c);
+    case Multiple:		return u.multiple.closure (c);
+    case Alternate:		return u.alternate.closure (c);
+    case Ligature:		return u.ligature.closure (c);
+    case Context:		return u.c.closure (c);
+    case ChainContext:		return u.chainContext.closure (c);
+    case Extension:		return u.extension.closure (c);
+    case ReverseChainSingle:	return u.reverseChainContextSingle.closure (c);
+    default:return false;
+    }
+  }
+
   inline bool would_apply (hb_codepoint_t glyph_id,
 			   unsigned int lookup_type) const
   {
@@ -860,6 +1011,15 @@ struct SubstLookup : Lookup
     return lookup_type_is_reverse (type);
   }
 
+  inline bool closure (hb_closure_context_t *c) const
+  {
+    unsigned int lookup_type = get_type ();
+    unsigned int count = get_subtable_count ();
+    bool ret = false;
+    for (unsigned int i = 0; i < count; i++)
+      ret = get_subtable (i).closure (c, lookup_type) || ret;
+    return ret;
+  }
 
   inline bool would_apply (hb_codepoint_t glyph_id) const
   {
@@ -984,6 +1144,10 @@ struct GSUB : GSUBGPOS
   static inline void substitute_start (hb_buffer_t *buffer);
   static inline void substitute_finish (hb_buffer_t *buffer);
 
+  inline bool closure_lookup (hb_closure_context_t *c,
+			      unsigned int          lookup_index) const
+  { return get_lookup (lookup_index).closure (c); }
+
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     if (unlikely (!GSUBGPOS::sanitize (c))) return false;
@@ -1048,6 +1212,21 @@ inline bool ExtensionSubst::is_reverse (void) const
   return SubstLookup::lookup_type_is_reverse (type);
 }
 
+static inline bool closure_lookup (hb_closure_context_t *c, unsigned int lookup_index)
+{
+  const GSUB &gsub = *(c->face->ot_layout->gsub);
+  const SubstLookup &l = gsub.get_lookup (lookup_index);
+
+  if (unlikely (c->nesting_level_left == 0))
+    return false;
+
+  c->nesting_level_left--;
+  bool ret = l.closure (c);
+  c->nesting_level_left++;
+
+  return ret;
+}
+
 static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index)
 {
   const GSUB &gsub = *(c->face->ot_layout->gsub);
diff --git a/src/hb-ot-layout-gsubgpos-private.hh b/src/hb-ot-layout-gsubgpos-private.hh
index 62ac29e..d5bc377 100644
--- a/src/hb-ot-layout-gsubgpos-private.hh
+++ b/src/hb-ot-layout-gsubgpos-private.hh
@@ -31,6 +31,8 @@
 
 #include "hb-buffer-private.hh"
 #include "hb-ot-layout-gdef-table.hh"
+#include "hb-ot-layout-closure.h"
+
 
 
 /* buffer var allocations */
@@ -45,6 +47,35 @@ static inline uint8_t allocate_lig_id (hb_buffer_t *buffer) {
 
 
 
+#ifndef HB_DEBUG_CLOSURE
+#define HB_DEBUG_CLOSURE (HB_DEBUG+0)
+#endif
+
+#define TRACE_CLOSURE() \
+	hb_auto_trace_t<HB_DEBUG_CLOSURE> trace (&c->debug_depth, "CLOSURE", this, NULL, HB_FUNC);
+
+
+
+struct hb_closure_context_t
+{
+  hb_face_t *face;
+  hb_glyph_map_t *glyphs;
+  unsigned int nesting_level_left;
+  unsigned int debug_depth;
+
+
+  hb_closure_context_t (hb_face_t *face_,
+			hb_glyph_map_t *glyphs_,
+		        unsigned int nesting_level_left_ = MAX_NESTING_LEVEL) :
+			  face (face_), glyphs (glyphs_),
+			  nesting_level_left (nesting_level_left_),
+			  debug_depth (0) {}
+};
+
+typedef bool (*closure_lookup_func_t) (hb_closure_context_t *c, unsigned int lookup_index);
+
+
+
 #ifndef HB_DEBUG_APPLY
 #define HB_DEBUG_APPLY (HB_DEBUG+0)
 #endif
@@ -56,7 +87,6 @@ static inline uint8_t allocate_lig_id (hb_buffer_t *buffer) {
 
 struct hb_apply_context_t
 {
-  unsigned int debug_depth;
   hb_font_t *font;
   hb_face_t *face;
   hb_buffer_t *buffer;
@@ -66,6 +96,7 @@ struct hb_apply_context_t
   unsigned int nesting_level_left;
   unsigned int lookup_props;
   unsigned int property; /* propety of first glyph */
+  unsigned int debug_depth;
 
 
   hb_apply_context_t (hb_font_t *font_,
@@ -81,7 +112,7 @@ struct hb_apply_context_t
 			context_length (context_length_),
 			nesting_level_left (nesting_level_left_),
 			lookup_props (l.get_props ()),
-			property (0) {}
+			property (0), debug_depth (0) {}
 
   hb_apply_context_t (const hb_apply_context_t &c, const Lookup &l) {
     *this = c;
@@ -427,8 +458,8 @@ struct Rule
     return inputCount.sanitize (c)
 	&& lookupCount.sanitize (c)
 	&& c->check_range (input,
-				 input[0].static_size * inputCount
-				 + lookupRecordX[0].static_size * lookupCount);
+			   input[0].static_size * inputCount
+			   + lookupRecordX[0].static_size * lookupCount);
   }
 
   private:
@@ -478,6 +509,14 @@ struct ContextFormat1
   friend struct Context;
 
   private:
+
+  inline bool closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+  {
+    TRACE_CLOSURE ();
+    /* TODO FILLME */
+    return false;
+  }
+
   inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
   {
     TRACE_APPLY ();
@@ -517,6 +556,14 @@ struct ContextFormat2
   friend struct Context;
 
   private:
+
+  inline bool closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+  {
+    TRACE_CLOSURE ();
+    /* TODO FILLME */
+    return false;
+  }
+
   inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
   {
     TRACE_APPLY ();
@@ -562,6 +609,14 @@ struct ContextFormat3
   friend struct Context;
 
   private:
+
+  inline bool closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+  {
+    TRACE_CLOSURE ();
+    /* TODO FILLME */
+    return false;
+  }
+
   inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
   {
     TRACE_APPLY ();
@@ -608,6 +663,18 @@ struct ContextFormat3
 struct Context
 {
   protected:
+
+  inline bool closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+  {
+    TRACE_CLOSURE ();
+    switch (u.format) {
+    case 1: return u.format1.closure (c, closure_func);
+    case 2: return u.format2.closure (c, closure_func);
+    case 3: return u.format3.closure (c, closure_func);
+    default:return false;
+    }
+  }
+
   inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
   {
     TRACE_APPLY ();
@@ -765,6 +832,14 @@ struct ChainContextFormat1
   friend struct ChainContext;
 
   private:
+
+  inline bool closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+  {
+    TRACE_CLOSURE ();
+    /* TODO FILLME */
+    return false;
+  }
+
   inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
   {
     TRACE_APPLY ();
@@ -803,6 +878,14 @@ struct ChainContextFormat2
   friend struct ChainContext;
 
   private:
+
+  inline bool closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+  {
+    TRACE_CLOSURE ();
+    /* TODO FILLME */
+    return false;
+  }
+
   inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
   {
     TRACE_APPLY ();
@@ -864,6 +947,13 @@ struct ChainContextFormat3
 
   private:
 
+  inline bool closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+  {
+    TRACE_CLOSURE ();
+    /* TODO FILLME */
+    return false;
+  }
+
   inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
   {
     TRACE_APPLY ();
@@ -922,6 +1012,18 @@ struct ChainContextFormat3
 struct ChainContext
 {
   protected:
+
+  inline bool closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+  {
+    TRACE_CLOSURE ();
+    switch (u.format) {
+    case 1: return u.format1.closure (c, closure_func);
+    case 2: return u.format2.closure (c, closure_func);
+    case 3: return u.format3.closure (c, closure_func);
+    default:return false;
+    }
+  }
+
   inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
   {
     TRACE_APPLY ();
diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index f3e0713..e515890 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -469,6 +469,14 @@ hb_ot_layout_substitute_finish (hb_buffer_t  *buffer HB_UNUSED)
   GSUB::substitute_finish (buffer);
 }
 
+hb_bool_t
+hb_ot_layout_closure_lookup (hb_face_t      *face,
+			     hb_glyph_map_t *glyphs,
+			     unsigned int    lookup_index)
+{
+  hb_closure_context_t c (face, glyphs);
+  return _get_gsub (face).closure_lookup (&c, lookup_index);
+}
 
 /*
  * GPOS
commit 7d50d502635d7c95e6bd091e7d4cc993f0853f76
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Mon Apr 23 13:02:14 2012 -0400

    Add Coverage iterators

diff --git a/src/hb-ot-layout-common-private.hh b/src/hb-ot-layout-common-private.hh
index ff967ea..48840f2 100644
--- a/src/hb-ot-layout-common-private.hh
+++ b/src/hb-ot-layout-common-private.hh
@@ -354,6 +354,16 @@ struct CoverageFormat1
     return glyphArray.sanitize (c);
   }
 
+  struct Iter {
+    void init (const struct CoverageFormat1 &c_) { c = &c_; i = 0; };
+    bool more (void) { return i < c->glyphArray.len; }
+    uint16_t next (void) { return c->glyphArray[i++]; }
+
+    private:
+    const struct CoverageFormat1 *c;
+    unsigned int i;
+  };
+
   private:
   USHORT	coverageFormat;	/* Format identifier--format = 1 */
   SortedArrayOf<GlyphID>
@@ -382,6 +392,23 @@ struct CoverageFormat2
     return rangeRecord.sanitize (c);
   }
 
+  struct Iter {
+    void init (const CoverageFormat2 &c_) { c = &c_; i = 0; j = c->rangeRecord.len ? c_.rangeRecord[0].start : 0; }
+    bool more (void) { return i < c->rangeRecord.len ||
+			      (i == c->rangeRecord.len && j < c->rangeRecord[i].end); }
+    uint16_t next (void) {
+      if (j == c->rangeRecord[i].end) {
+        i++;
+	j = c->rangeRecord[i].start;
+      }
+      return j++;
+    }
+
+    private:
+    const struct CoverageFormat2 *c;
+    unsigned int i, j;
+  };
+
   private:
   USHORT	coverageFormat;	/* Format identifier--format = 2 */
   SortedArrayOf<RangeRecord>
@@ -415,6 +442,38 @@ struct Coverage
     }
   }
 
+  struct Iter {
+    void init (const Coverage &c_) {
+      format = c_.u.format;
+      switch (format) {
+      case 1: return u.format1.init (c_.u.format1);
+      case 2: return u.format2.init (c_.u.format2);
+      default:return;
+      }
+    }
+    bool more (void) {
+      switch (format) {
+      case 1: return u.format1.more ();
+      case 2: return u.format2.more ();
+      default:return true;
+      }
+    }
+    uint16_t next (void) {
+      switch (format) {
+      case 1: return u.format1.next ();
+      case 2: return u.format2.next ();
+      default:return true;
+      }
+    }
+
+    private:
+    USHORT format;
+    union {
+    CoverageFormat1::Iter	format1;
+    CoverageFormat2::Iter	format2;
+    } u;
+  };
+
   private:
   union {
   USHORT		format;		/* Format identifier */
commit 3ed4634ec349fa9e943ad23718c04be4dd4bba62
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu Apr 19 22:34:06 2012 -0400

    Add Indic inspection tool

diff --git a/src/Makefile.am b/src/Makefile.am
index 9311ab7..f53505e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -67,6 +67,7 @@ HBSOURCES += \
 	hb-ot-shape-complex-arabic-table.hh \
 	hb-ot-shape-complex-indic.cc \
 	hb-ot-shape-complex-indic-machine.hh \
+	hb-ot-shape-complex-indic-private.hh \
 	hb-ot-shape-complex-indic-table.hh \
 	hb-ot-shape-complex-misc.cc \
 	hb-ot-shape-complex-private.hh \
@@ -169,13 +170,17 @@ hb-ot-shape-complex-indic-machine.hh: hb-ot-shape-complex-indic-machine.rl
 	$(AM_V_GEN)$(top_srcdir)/missing --run ragel -e -F1 -o "$@.tmp" "$<" && \
 	mv "$@.tmp" "$@" || ( $(RM) "$@.tmp" && false )
 
-noinst_PROGRAMS = main
+noinst_PROGRAMS = main indic
 bin_PROGRAMS =
 
 main_SOURCES = main.cc
 main_CPPFLAGS = $(HBCFLAGS)
 main_LDADD = libharfbuzz.la $(HBLIBS)
 
+indic_SOURCES = indic.cc
+indic_CPPFLAGS = $(HBCFLAGS)
+indic_LDADD = libharfbuzz.la $(HBLIBS)
+
 dist_check_SCRIPTS = \
 	check-c-linkage-decls.sh \
 	check-header-guards.sh \
diff --git a/src/hb-ot-shape-complex-indic-private.hh b/src/hb-ot-shape-complex-indic-private.hh
new file mode 100644
index 0000000..b7bce17
--- /dev/null
+++ b/src/hb-ot-shape-complex-indic-private.hh
@@ -0,0 +1,264 @@
+/*
+ * Copyright © 2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_SHAPE_COMPLEX_INDIC_PRIVATE_HH
+#define HB_OT_SHAPE_COMPLEX_INDIC_PRIVATE_HH
+
+#include "hb-private.hh"
+
+
+#include "hb-ot-shape-complex-private.hh"
+
+
+/* buffer var allocations */
+#define indic_category() complex_var_persistent_u8_0() /* indic_category_t */
+#define indic_position() complex_var_persistent_u8_1() /* indic_matra_category_t */
+
+#define INDIC_TABLE_ELEMENT_TYPE uint8_t
+
+/* Cateories used in the OpenType spec:
+ * https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx
+ */
+/* Note: This enum is duplicated in the -machine.rl source file.
+ * Not sure how to avoid duplication. */
+enum indic_category_t {
+  OT_X = 0,
+  OT_C,
+  OT_Ra, /* Not explicitly listed in the OT spec, but used in the grammar. */
+  OT_V,
+  OT_N,
+  OT_H,
+  OT_ZWNJ,
+  OT_ZWJ,
+  OT_M,
+  OT_SM,
+  OT_VD,
+  OT_A,
+  OT_NBSP
+};
+
+/* Visual positions in a syllable from left to right. */
+enum indic_position_t {
+  POS_PRE = 1,
+  POS_BASE = 2,
+  POS_ABOVE = 3,
+  POS_BELOW = 4,
+  POS_POST = 5
+};
+
+/* Categories used in IndicSyllabicCategory.txt from UCD */
+/* The assignments are guesswork */
+enum indic_syllabic_category_t {
+  INDIC_SYLLABIC_CATEGORY_OTHER			= OT_X,
+
+  INDIC_SYLLABIC_CATEGORY_AVAGRAHA		= OT_X,
+  INDIC_SYLLABIC_CATEGORY_BINDU			= OT_SM,
+  INDIC_SYLLABIC_CATEGORY_CONSONANT		= OT_C,
+  INDIC_SYLLABIC_CATEGORY_CONSONANT_DEAD	= OT_C,
+  INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL	= OT_C,
+  INDIC_SYLLABIC_CATEGORY_CONSONANT_HEAD_LETTER	= OT_C,
+  INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL	= OT_C,
+  INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER	= OT_NBSP,
+  INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED	= OT_C,
+  INDIC_SYLLABIC_CATEGORY_CONSONANT_REPHA	= OT_C,
+  INDIC_SYLLABIC_CATEGORY_MODIFYING_LETTER	= OT_X,
+  INDIC_SYLLABIC_CATEGORY_NUKTA			= OT_N,
+  INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER	= OT_X,
+  INDIC_SYLLABIC_CATEGORY_TONE_LETTER		= OT_X,
+  INDIC_SYLLABIC_CATEGORY_TONE_MARK		= OT_X,
+  INDIC_SYLLABIC_CATEGORY_VIRAMA		= OT_H,
+  INDIC_SYLLABIC_CATEGORY_VISARGA		= OT_SM,
+  INDIC_SYLLABIC_CATEGORY_VOWEL			= OT_V,
+  INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT	= OT_M,
+  INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT	= OT_V
+};
+
+/* Categories used in IndicSMatraCategory.txt from UCD */
+enum indic_matra_category_t {
+  INDIC_MATRA_CATEGORY_NOT_APPLICABLE		= POS_BASE,
+
+  INDIC_MATRA_CATEGORY_LEFT			= POS_PRE - 1, /* Move *before* existing "pre" chars */
+  INDIC_MATRA_CATEGORY_TOP			= POS_ABOVE,
+  INDIC_MATRA_CATEGORY_BOTTOM			= POS_BELOW,
+  INDIC_MATRA_CATEGORY_RIGHT			= POS_POST,
+
+  /* We don't really care much about these since we decompose them
+   * in the generic pre-shaping layer.  They will only be used if
+   * the font does not cover the decomposition.  In which case, we
+   * define these as aliases to the place we want the split-matra
+   * glyph to show up.  Quite arbitrary.
+   *
+   * TODO: There are some split matras without Unicode decompositions.
+   * We have to figure out what to do with them.
+   */
+  INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT		= 8 | INDIC_MATRA_CATEGORY_BOTTOM,
+  INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT		= 8 | INDIC_MATRA_CATEGORY_LEFT,
+  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM		= 8 | INDIC_MATRA_CATEGORY_BOTTOM,
+  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT	= 8 | INDIC_MATRA_CATEGORY_BOTTOM,
+  INDIC_MATRA_CATEGORY_TOP_AND_LEFT		= 8 | INDIC_MATRA_CATEGORY_LEFT,
+  INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT	= 8 | INDIC_MATRA_CATEGORY_LEFT,
+  INDIC_MATRA_CATEGORY_TOP_AND_RIGHT		= 8 | INDIC_MATRA_CATEGORY_RIGHT,
+
+  INDIC_MATRA_CATEGORY_INVISIBLE		= INDIC_MATRA_CATEGORY_NOT_APPLICABLE,
+  INDIC_MATRA_CATEGORY_OVERSTRUCK		= INDIC_MATRA_CATEGORY_NOT_APPLICABLE,
+  INDIC_MATRA_CATEGORY_VISUAL_ORDER_LEFT	= INDIC_MATRA_CATEGORY_NOT_APPLICABLE
+};
+
+/* Note: We use ASSERT_STATIC_EXPR_ZERO() instead of ASSERT_STATIC_EXPR() and the comma operation
+ * because gcc fails to optimize the latter and fills the table in at runtime. */
+#define INDIC_COMBINE_CATEGORIES(S,M) \
+  (ASSERT_STATIC_EXPR_ZERO (M == INDIC_MATRA_CATEGORY_NOT_APPLICABLE || (S == INDIC_SYLLABIC_CATEGORY_VIRAMA || S == INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT)) + \
+   ASSERT_STATIC_EXPR_ZERO (S < 16 && M < 16) + \
+   ((M << 4) | S))
+
+
+#include "hb-ot-shape-complex-indic-table.hh"
+
+/* XXX
+ * This is a hack for now.  We should:
+ * 1. Move this data into the main Indic table,
+ * and/or
+ * 2. Probe font lookups to determine consonant positions.
+ */
+static const struct consonant_position_t {
+  hb_codepoint_t u;
+  indic_position_t position;
+} consonant_positions[] = {
+  {0x0930, POS_BELOW},
+  {0x09AC, POS_BELOW},
+  {0x09AF, POS_POST},
+  {0x09B0, POS_BELOW},
+  {0x09F0, POS_BELOW},
+  {0x0A2F, POS_POST},
+  {0x0A30, POS_BELOW},
+  {0x0A35, POS_BELOW},
+  {0x0A39, POS_BELOW},
+  {0x0AB0, POS_BELOW},
+  {0x0B24, POS_BELOW},
+  {0x0B28, POS_BELOW},
+  {0x0B2C, POS_BELOW},
+  {0x0B2D, POS_BELOW},
+  {0x0B2E, POS_BELOW},
+  {0x0B2F, POS_POST},
+  {0x0B30, POS_BELOW},
+  {0x0B32, POS_BELOW},
+  {0x0B33, POS_BELOW},
+  {0x0B5F, POS_POST},
+  {0x0B71, POS_BELOW},
+  {0x0C15, POS_BELOW},
+  {0x0C16, POS_BELOW},
+  {0x0C17, POS_BELOW},
+  {0x0C18, POS_BELOW},
+  {0x0C19, POS_BELOW},
+  {0x0C1A, POS_BELOW},
+  {0x0C1B, POS_BELOW},
+  {0x0C1C, POS_BELOW},
+  {0x0C1D, POS_BELOW},
+  {0x0C1E, POS_BELOW},
+  {0x0C1F, POS_BELOW},
+  {0x0C20, POS_BELOW},
+  {0x0C21, POS_BELOW},
+  {0x0C22, POS_BELOW},
+  {0x0C23, POS_BELOW},
+  {0x0C24, POS_BELOW},
+  {0x0C25, POS_BELOW},
+  {0x0C26, POS_BELOW},
+  {0x0C27, POS_BELOW},
+  {0x0C28, POS_BELOW},
+  {0x0C2A, POS_BELOW},
+  {0x0C2B, POS_BELOW},
+  {0x0C2C, POS_BELOW},
+  {0x0C2D, POS_BELOW},
+  {0x0C2E, POS_BELOW},
+  {0x0C2F, POS_BELOW},
+  {0x0C30, POS_BELOW},
+  {0x0C32, POS_BELOW},
+  {0x0C33, POS_BELOW},
+  {0x0C35, POS_BELOW},
+  {0x0C36, POS_BELOW},
+  {0x0C37, POS_BELOW},
+  {0x0C38, POS_BELOW},
+  {0x0C39, POS_BELOW},
+  {0x0C95, POS_BELOW},
+  {0x0C96, POS_BELOW},
+  {0x0C97, POS_BELOW},
+  {0x0C98, POS_BELOW},
+  {0x0C99, POS_BELOW},
+  {0x0C9A, POS_BELOW},
+  {0x0C9B, POS_BELOW},
+  {0x0C9C, POS_BELOW},
+  {0x0C9D, POS_BELOW},
+  {0x0C9E, POS_BELOW},
+  {0x0C9F, POS_BELOW},
+  {0x0CA0, POS_BELOW},
+  {0x0CA1, POS_BELOW},
+  {0x0CA2, POS_BELOW},
+  {0x0CA3, POS_BELOW},
+  {0x0CA4, POS_BELOW},
+  {0x0CA5, POS_BELOW},
+  {0x0CA6, POS_BELOW},
+  {0x0CA7, POS_BELOW},
+  {0x0CA8, POS_BELOW},
+  {0x0CAA, POS_BELOW},
+  {0x0CAB, POS_BELOW},
+  {0x0CAC, POS_BELOW},
+  {0x0CAD, POS_BELOW},
+  {0x0CAE, POS_BELOW},
+  {0x0CAF, POS_BELOW},
+  {0x0CB0, POS_BELOW},
+  {0x0CB2, POS_BELOW},
+  {0x0CB3, POS_BELOW},
+  {0x0CB5, POS_BELOW},
+  {0x0CB6, POS_BELOW},
+  {0x0CB7, POS_BELOW},
+  {0x0CB8, POS_BELOW},
+  {0x0CB9, POS_BELOW},
+  {0x0CDE, POS_BELOW},
+  {0x0D2F, POS_POST},
+  {0x0D30, POS_POST},
+  {0x0D32, POS_BELOW},
+  {0x0D35, POS_POST},
+};
+
+/* XXX
+ * This is a hack for now.  We should move this data into the main Indic table.
+ */
+static const hb_codepoint_t ra_chars[] = {
+  0x0930, /* Devanagari */
+  0x09B0, /* Bengali */
+  0x09F0, /* Bengali */
+//0x09F1, /* Bengali */
+//0x0A30, /* Gurmukhi */
+  0x0AB0, /* Gujarati */
+  0x0B30, /* Oriya */
+//0x0BB0, /* Tamil */
+//0x0C30, /* Telugu */
+  0x0CB0, /* Kannada */
+//0x0D30, /* Malayalam */
+};
+
+
+#endif /* HB_OT_SHAPE_COMPLEX_INDIC_PRIVATE_HH */
diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 06b8d48..09222f7 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -24,235 +24,8 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-ot-shape-complex-private.hh"
 
-
-
-/* buffer var allocations */
-#define indic_category() complex_var_persistent_u8_0() /* indic_category_t */
-#define indic_position() complex_var_persistent_u8_1() /* indic_matra_category_t */
-
-#define INDIC_TABLE_ELEMENT_TYPE uint8_t
-
-/* Cateories used in the OpenType spec:
- * https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx
- */
-/* Note: This enum is duplicated in the -machine.rl source file.
- * Not sure how to avoid duplication. */
-enum indic_category_t {
-  OT_X = 0,
-  OT_C,
-  OT_Ra, /* Not explicitly listed in the OT spec, but used in the grammar. */
-  OT_V,
-  OT_N,
-  OT_H,
-  OT_ZWNJ,
-  OT_ZWJ,
-  OT_M,
-  OT_SM,
-  OT_VD,
-  OT_A,
-  OT_NBSP
-};
-
-/* Visual positions in a syllable from left to right. */
-enum indic_position_t {
-  POS_PRE = 1,
-  POS_BASE = 2,
-  POS_ABOVE = 3,
-  POS_BELOW = 4,
-  POS_POST = 5
-};
-
-/* Categories used in IndicSyllabicCategory.txt from UCD */
-/* The assignments are guesswork */
-enum indic_syllabic_category_t {
-  INDIC_SYLLABIC_CATEGORY_OTHER			= OT_X,
-
-  INDIC_SYLLABIC_CATEGORY_AVAGRAHA		= OT_X,
-  INDIC_SYLLABIC_CATEGORY_BINDU			= OT_SM,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT		= OT_C,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_DEAD	= OT_C,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL	= OT_C,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_HEAD_LETTER	= OT_C,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL	= OT_C,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER	= OT_NBSP,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED	= OT_C,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_REPHA	= OT_C,
-  INDIC_SYLLABIC_CATEGORY_MODIFYING_LETTER	= OT_X,
-  INDIC_SYLLABIC_CATEGORY_NUKTA			= OT_N,
-  INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER	= OT_X,
-  INDIC_SYLLABIC_CATEGORY_TONE_LETTER		= OT_X,
-  INDIC_SYLLABIC_CATEGORY_TONE_MARK		= OT_X,
-  INDIC_SYLLABIC_CATEGORY_VIRAMA		= OT_H,
-  INDIC_SYLLABIC_CATEGORY_VISARGA		= OT_SM,
-  INDIC_SYLLABIC_CATEGORY_VOWEL			= OT_V,
-  INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT	= OT_M,
-  INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT	= OT_V
-};
-
-/* Categories used in IndicSMatraCategory.txt from UCD */
-enum indic_matra_category_t {
-  INDIC_MATRA_CATEGORY_NOT_APPLICABLE		= POS_BASE,
-
-  INDIC_MATRA_CATEGORY_LEFT			= POS_PRE - 1, /* Move *before* existing "pre" chars */
-  INDIC_MATRA_CATEGORY_TOP			= POS_ABOVE,
-  INDIC_MATRA_CATEGORY_BOTTOM			= POS_BELOW,
-  INDIC_MATRA_CATEGORY_RIGHT			= POS_POST,
-
-  /* We don't really care much about these since we decompose them
-   * in the generic pre-shaping layer.  They will only be used if
-   * the font does not cover the decomposition.  In which case, we
-   * define these as aliases to the place we want the split-matra
-   * glyph to show up.  Quite arbitrary.
-   *
-   * TODO: There are some split matras without Unicode decompositions.
-   * We have to figure out what to do with them.
-   */
-  INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT		= 8 | INDIC_MATRA_CATEGORY_BOTTOM,
-  INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT		= 8 | INDIC_MATRA_CATEGORY_LEFT,
-  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM		= 8 | INDIC_MATRA_CATEGORY_BOTTOM,
-  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT	= 8 | INDIC_MATRA_CATEGORY_BOTTOM,
-  INDIC_MATRA_CATEGORY_TOP_AND_LEFT		= 8 | INDIC_MATRA_CATEGORY_LEFT,
-  INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT	= 8 | INDIC_MATRA_CATEGORY_LEFT,
-  INDIC_MATRA_CATEGORY_TOP_AND_RIGHT		= 8 | INDIC_MATRA_CATEGORY_RIGHT,
-
-  INDIC_MATRA_CATEGORY_INVISIBLE		= INDIC_MATRA_CATEGORY_NOT_APPLICABLE,
-  INDIC_MATRA_CATEGORY_OVERSTRUCK		= INDIC_MATRA_CATEGORY_NOT_APPLICABLE,
-  INDIC_MATRA_CATEGORY_VISUAL_ORDER_LEFT	= INDIC_MATRA_CATEGORY_NOT_APPLICABLE
-};
-
-/* Note: We use ASSERT_STATIC_EXPR_ZERO() instead of ASSERT_STATIC_EXPR() and the comma operation
- * because gcc fails to optimize the latter and fills the table in at runtime. */
-#define INDIC_COMBINE_CATEGORIES(S,M) \
-  (ASSERT_STATIC_EXPR_ZERO (M == INDIC_MATRA_CATEGORY_NOT_APPLICABLE || (S == INDIC_SYLLABIC_CATEGORY_VIRAMA || S == INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT)) + \
-   ASSERT_STATIC_EXPR_ZERO (S < 16 && M < 16) + \
-   ((M << 4) | S))
-
-#include "hb-ot-shape-complex-indic-table.hh"
-
-/* XXX
- * This is a hack for now.  We should:
- * 1. Move this data into the main Indic table,
- * and/or
- * 2. Probe font lookups to determine consonant positions.
- */
-static const struct consonant_position_t {
-  hb_codepoint_t u;
-  indic_position_t position;
-} consonant_positions[] = {
-  {0x0930, POS_BELOW},
-  {0x09AC, POS_BELOW},
-  {0x09AF, POS_POST},
-  {0x09B0, POS_BELOW},
-  {0x09F0, POS_BELOW},
-  {0x0A2F, POS_POST},
-  {0x0A30, POS_BELOW},
-  {0x0A35, POS_BELOW},
-  {0x0A39, POS_BELOW},
-  {0x0AB0, POS_BELOW},
-  {0x0B24, POS_BELOW},
-  {0x0B28, POS_BELOW},
-  {0x0B2C, POS_BELOW},
-  {0x0B2D, POS_BELOW},
-  {0x0B2E, POS_BELOW},
-  {0x0B2F, POS_POST},
-  {0x0B30, POS_BELOW},
-  {0x0B32, POS_BELOW},
-  {0x0B33, POS_BELOW},
-  {0x0B5F, POS_POST},
-  {0x0B71, POS_BELOW},
-  {0x0C15, POS_BELOW},
-  {0x0C16, POS_BELOW},
-  {0x0C17, POS_BELOW},
-  {0x0C18, POS_BELOW},
-  {0x0C19, POS_BELOW},
-  {0x0C1A, POS_BELOW},
-  {0x0C1B, POS_BELOW},
-  {0x0C1C, POS_BELOW},
-  {0x0C1D, POS_BELOW},
-  {0x0C1E, POS_BELOW},
-  {0x0C1F, POS_BELOW},
-  {0x0C20, POS_BELOW},
-  {0x0C21, POS_BELOW},
-  {0x0C22, POS_BELOW},
-  {0x0C23, POS_BELOW},
-  {0x0C24, POS_BELOW},
-  {0x0C25, POS_BELOW},
-  {0x0C26, POS_BELOW},
-  {0x0C27, POS_BELOW},
-  {0x0C28, POS_BELOW},
-  {0x0C2A, POS_BELOW},
-  {0x0C2B, POS_BELOW},
-  {0x0C2C, POS_BELOW},
-  {0x0C2D, POS_BELOW},
-  {0x0C2E, POS_BELOW},
-  {0x0C2F, POS_BELOW},
-  {0x0C30, POS_BELOW},
-  {0x0C32, POS_BELOW},
-  {0x0C33, POS_BELOW},
-  {0x0C35, POS_BELOW},
-  {0x0C36, POS_BELOW},
-  {0x0C37, POS_BELOW},
-  {0x0C38, POS_BELOW},
-  {0x0C39, POS_BELOW},
-  {0x0C95, POS_BELOW},
-  {0x0C96, POS_BELOW},
-  {0x0C97, POS_BELOW},
-  {0x0C98, POS_BELOW},
-  {0x0C99, POS_BELOW},
-  {0x0C9A, POS_BELOW},
-  {0x0C9B, POS_BELOW},
-  {0x0C9C, POS_BELOW},
-  {0x0C9D, POS_BELOW},
-  {0x0C9E, POS_BELOW},
-  {0x0C9F, POS_BELOW},
-  {0x0CA0, POS_BELOW},
-  {0x0CA1, POS_BELOW},
-  {0x0CA2, POS_BELOW},
-  {0x0CA3, POS_BELOW},
-  {0x0CA4, POS_BELOW},
-  {0x0CA5, POS_BELOW},
-  {0x0CA6, POS_BELOW},
-  {0x0CA7, POS_BELOW},
-  {0x0CA8, POS_BELOW},
-  {0x0CAA, POS_BELOW},
-  {0x0CAB, POS_BELOW},
-  {0x0CAC, POS_BELOW},
-  {0x0CAD, POS_BELOW},
-  {0x0CAE, POS_BELOW},
-  {0x0CAF, POS_BELOW},
-  {0x0CB0, POS_BELOW},
-  {0x0CB2, POS_BELOW},
-  {0x0CB3, POS_BELOW},
-  {0x0CB5, POS_BELOW},
-  {0x0CB6, POS_BELOW},
-  {0x0CB7, POS_BELOW},
-  {0x0CB8, POS_BELOW},
-  {0x0CB9, POS_BELOW},
-  {0x0CDE, POS_BELOW},
-  {0x0D2F, POS_POST},
-  {0x0D30, POS_POST},
-  {0x0D32, POS_BELOW},
-  {0x0D35, POS_POST},
-};
-
-/* XXX
- * This is a hack for now.  We should move this data into the main Indic table.
- */
-static const hb_codepoint_t ra_chars[] = {
-  0x0930, /* Devanagari */
-  0x09B0, /* Bengali */
-  0x09F0, /* Bengali */
-//0x09F1, /* Bengali */
-//0x0A30, /* Gurmukhi */
-  0x0AB0, /* Gujarati */
-  0x0B30, /* Oriya */
-//0x0BB0, /* Tamil */
-//0x0C30, /* Telugu */
-  0x0CB0, /* Kannada */
-//0x0D30, /* Malayalam */
-};
+#include "hb-ot-shape-complex-indic-private.hh"
 
 static int
 compare_codepoint (const void *pa, const void *pb)
diff --git a/src/indic.cc b/src/indic.cc
new file mode 100644
index 0000000..6c6e05a
--- /dev/null
+++ b/src/indic.cc
@@ -0,0 +1,46 @@
+/*
+ * Copyright © 2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "hb-ot-shape-complex-indic-private.hh"
+
+int
+main (void)
+{
+  hb_unicode_funcs_t *funcs = hb_unicode_funcs_get_default ();
+
+  printf ("There are split matras without a Unicode decomposition:\n");
+  for (hb_codepoint_t u = 0; u < 0x110000; u++)
+  {
+    unsigned int type = get_indic_categories (u);
+
+    unsigned int category = type & 0x0F;
+    unsigned int position = type >> 4;
+
+    hb_codepoint_t a, b;
+    if ((position & 8) && !hb_unicode_decompose (funcs, u, &a, &b))
+      printf ("U+%04X\n", u);
+  }
+}
commit a06411ecf93c7e5256e363eef3ef69554896dd55
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu Apr 19 22:28:25 2012 -0400

    Minor matra renumbering
    
    Should have no visible effect.

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 3a57bb8..06b8d48 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -58,10 +58,10 @@ enum indic_category_t {
 /* Visual positions in a syllable from left to right. */
 enum indic_position_t {
   POS_PRE = 1,
-  POS_BASE = 3,
-  POS_ABOVE = 5,
-  POS_BELOW = 7,
-  POS_POST = 9
+  POS_BASE = 2,
+  POS_ABOVE = 3,
+  POS_BELOW = 4,
+  POS_POST = 5
 };
 
 /* Categories used in IndicSyllabicCategory.txt from UCD */
@@ -104,14 +104,18 @@ enum indic_matra_category_t {
    * in the generic pre-shaping layer.  They will only be used if
    * the font does not cover the decomposition.  In which case, we
    * define these as aliases to the place we want the split-matra
-   * glyph to show up.  Quite arbitrary. */
-  INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT		= INDIC_MATRA_CATEGORY_BOTTOM,
-  INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT		= INDIC_MATRA_CATEGORY_LEFT,
-  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM		= INDIC_MATRA_CATEGORY_BOTTOM,
-  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT	= INDIC_MATRA_CATEGORY_BOTTOM,
-  INDIC_MATRA_CATEGORY_TOP_AND_LEFT		= INDIC_MATRA_CATEGORY_LEFT,
-  INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT	= INDIC_MATRA_CATEGORY_LEFT,
-  INDIC_MATRA_CATEGORY_TOP_AND_RIGHT		= INDIC_MATRA_CATEGORY_RIGHT,
+   * glyph to show up.  Quite arbitrary.
+   *
+   * TODO: There are some split matras without Unicode decompositions.
+   * We have to figure out what to do with them.
+   */
+  INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT		= 8 | INDIC_MATRA_CATEGORY_BOTTOM,
+  INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT		= 8 | INDIC_MATRA_CATEGORY_LEFT,
+  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM		= 8 | INDIC_MATRA_CATEGORY_BOTTOM,
+  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT	= 8 | INDIC_MATRA_CATEGORY_BOTTOM,
+  INDIC_MATRA_CATEGORY_TOP_AND_LEFT		= 8 | INDIC_MATRA_CATEGORY_LEFT,
+  INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT	= 8 | INDIC_MATRA_CATEGORY_LEFT,
+  INDIC_MATRA_CATEGORY_TOP_AND_RIGHT		= 8 | INDIC_MATRA_CATEGORY_RIGHT,
 
   INDIC_MATRA_CATEGORY_INVISIBLE		= INDIC_MATRA_CATEGORY_NOT_APPLICABLE,
   INDIC_MATRA_CATEGORY_OVERSTRUCK		= INDIC_MATRA_CATEGORY_NOT_APPLICABLE,
@@ -392,7 +396,7 @@ _hb_ot_shape_complex_setup_masks_indic (hb_ot_map_t *map, hb_buffer_t *buffer, h
     unsigned int type = get_indic_categories (buffer->info[i].codepoint);
 
     buffer->info[i].indic_category() = type & 0x0F;
-    buffer->info[i].indic_position() = type >> 4;
+    buffer->info[i].indic_position() = (type >> 4) & 7;
 
     if (buffer->info[i].indic_category() == OT_C) {
       buffer->info[i].indic_position() = consonant_position (buffer->info[i].codepoint);
commit 36608941f3cc530fea57282fa175e4cc3b4c66c6
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu Apr 19 22:21:38 2012 -0400

    Add GSUB "would_apply" API
    
    To be used in the Indic shaper later.  Unused for now.

diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh
index 8f01e13..f2210a0 100644
--- a/src/hb-ot-layout-gsub-table.hh
+++ b/src/hb-ot-layout-gsub-table.hh
@@ -39,6 +39,11 @@ struct SingleSubstFormat1
 
   private:
 
+  inline bool would_apply (hb_codepoint_t glyph_id) const
+  {
+    return (this+coverage) (glyph_id) != NOT_COVERED;
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
@@ -78,6 +83,11 @@ struct SingleSubstFormat2
 
   private:
 
+  inline bool would_apply (hb_codepoint_t glyph_id) const
+  {
+    return (this+coverage) (glyph_id) != NOT_COVERED;
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
@@ -119,6 +129,15 @@ struct SingleSubst
 
   private:
 
+  inline bool would_apply (hb_codepoint_t glyph_id) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.would_apply (glyph_id);
+    case 2: return u.format2.would_apply (glyph_id);
+    default:return false;
+    }
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
@@ -185,6 +204,11 @@ struct MultipleSubstFormat1
 
   private:
 
+  inline bool would_apply (hb_codepoint_t glyph_id) const
+  {
+    return (this+coverage) (glyph_id) != NOT_COVERED;
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
@@ -220,6 +244,14 @@ struct MultipleSubst
 
   private:
 
+  inline bool would_apply (hb_codepoint_t glyph_id) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.would_apply (glyph_id);
+    default:return false;
+    }
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
@@ -255,12 +287,15 @@ struct AlternateSubstFormat1
 
   private:
 
+  inline bool would_apply (hb_codepoint_t glyph_id) const
+  {
+    return (this+coverage) (glyph_id) != NOT_COVERED;
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     hb_codepoint_t glyph_id = c->buffer->info[c->buffer->idx].codepoint;
-    hb_mask_t glyph_mask = c->buffer->info[c->buffer->idx].mask;
-    hb_mask_t lookup_mask = c->lookup_mask;
 
     unsigned int index = (this+coverage) (glyph_id);
     if (likely (index == NOT_COVERED))
@@ -271,6 +306,9 @@ struct AlternateSubstFormat1
     if (unlikely (!alt_set.len))
       return false;
 
+    hb_mask_t glyph_mask = c->buffer->info[c->buffer->idx].mask;
+    hb_mask_t lookup_mask = c->lookup_mask;
+
     /* Note: This breaks badly if two features enabled this lookup together. */
     unsigned int shift = _hb_ctz (lookup_mask);
     unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift);
@@ -309,6 +347,14 @@ struct AlternateSubst
 
   private:
 
+  inline bool would_apply (hb_codepoint_t glyph_id) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.would_apply (glyph_id);
+    default:return false;
+    }
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
@@ -340,6 +386,12 @@ struct Ligature
   friend struct LigatureSet;
 
   private:
+
+  inline bool would_apply (hb_codepoint_t second) const
+  {
+    return component.len == 2 && component[1] == second;
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
@@ -429,6 +481,19 @@ struct LigatureSet
   friend struct LigatureSubstFormat1;
 
   private:
+
+  inline bool would_apply (hb_codepoint_t second) const
+  {
+    unsigned int num_ligs = ligature.len;
+    for (unsigned int i = 0; i < num_ligs; i++)
+    {
+      const Ligature &lig = this+ligature[i];
+      if (lig.would_apply (second))
+        return true;
+    }
+    return false;
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
@@ -462,6 +527,14 @@ struct LigatureSubstFormat1
   friend struct LigatureSubst;
 
   private:
+
+  inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const
+  {
+    unsigned int index;
+    return (index = (this+coverage) (first)) != NOT_COVERED &&
+	   (this+ligatureSet[index]).would_apply (second);
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
@@ -498,6 +571,15 @@ struct LigatureSubst
   friend struct SubstLookupSubTable;
 
   private:
+
+  inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.would_apply (first, second);
+    default:return false;
+    }
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
@@ -564,6 +646,9 @@ struct ExtensionSubst : Extension
     return StructAtOffset<SubstLookupSubTable> (this, offset);
   }
 
+  inline bool would_apply (hb_codepoint_t glyph_id) const;
+  inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const;
+
   inline bool apply (hb_apply_context_t *c) const;
 
   inline bool sanitize (hb_sanitize_context_t *c);
@@ -689,6 +774,28 @@ struct SubstLookupSubTable
     ReverseChainSingle	= 8
   };
 
+  inline bool would_apply (hb_codepoint_t glyph_id,
+			   unsigned int lookup_type) const
+  {
+    switch (lookup_type) {
+    case Single:		return u.single.would_apply (glyph_id);
+    case Multiple:		return u.multiple.would_apply (glyph_id);
+    case Alternate:		return u.alternate.would_apply (glyph_id);
+    case Extension:		return u.extension.would_apply (glyph_id);
+    default:return false;
+    }
+  }
+  inline bool would_apply (hb_codepoint_t first,
+			   hb_codepoint_t second,
+			   unsigned int lookup_type) const
+  {
+    switch (lookup_type) {
+    case Ligature:		return u.ligature.would_apply (first, second);
+    case Extension:		return u.extension.would_apply (first, second);
+    default:return false;
+    }
+  }
+
   inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const
   {
     TRACE_APPLY ();
@@ -754,6 +861,25 @@ struct SubstLookup : Lookup
   }
 
 
+  inline bool would_apply (hb_codepoint_t glyph_id) const
+  {
+    unsigned int lookup_type = get_type ();
+    unsigned int count = get_subtable_count ();
+    for (unsigned int i = 0; i < count; i++)
+      if (get_subtable (i).would_apply (glyph_id, lookup_type))
+	return true;
+    return false;
+  }
+  inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const
+  {
+    unsigned int lookup_type = get_type ();
+    unsigned int count = get_subtable_count ();
+    for (unsigned int i = 0; i < count; i++)
+      if (get_subtable (i).would_apply (first, second, lookup_type))
+	return true;
+    return false;
+  }
+
   inline bool apply_once (hb_apply_context_t *c) const
   {
     unsigned int lookup_type = get_type ();
@@ -889,6 +1015,16 @@ GSUB::substitute_finish (hb_buffer_t *buffer)
 
 /* Out-of-class implementation for methods recursing */
 
+inline bool ExtensionSubst::would_apply (hb_codepoint_t glyph_id) const
+{
+  return get_subtable ().would_apply (glyph_id, get_type ());
+}
+
+inline bool ExtensionSubst::would_apply (hb_codepoint_t first, hb_codepoint_t second) const
+{
+  return get_subtable ().would_apply (first, second, get_type ());
+}
+
 inline bool ExtensionSubst::apply (hb_apply_context_t *c) const
 {
   TRACE_APPLY ();
commit a0d4caeb91fa5e5f2090db4efc35c64ff9a64789
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Apr 17 13:42:30 2012 -0400

    Minor

diff --git a/TODO b/TODO
index b0eac7a..d763c30 100644
--- a/TODO
+++ b/TODO
@@ -1,6 +1,9 @@
 General fixes:
 =============
 
+- Warn at compile time (and runtime with HB_DEBUG?) if no Unicode funcs
+  found / set.
+
 - In hb_shape(), assert if direction is INVALID.
 
 - Fix TT 'kern' on/off and GPOS interaction (move kerning before GPOS).
commit a5e40542ab9508f0ba6f822f1262d93fccb71f45
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Apr 17 12:37:19 2012 -0400

    Make font immutable in hb_shape()

diff --git a/src/hb-shape.cc b/src/hb-shape.cc
index 3d5f56c..d97028e 100644
--- a/src/hb-shape.cc
+++ b/src/hb-shape.cc
@@ -121,6 +121,8 @@ hb_shape_full (hb_font_t          *font,
 	       unsigned int        num_features,
 	       const char * const *shaper_list)
 {
+  hb_font_make_immutable (font); /* So we can safely cache stuff on it */
+
   if (likely (!shaper_list)) {
     for (unsigned int i = 0; i < ARRAY_LENGTH (shapers); i++)
       if (likely (shapers[i].func (font, buffer, features, num_features)))



More information about the HarfBuzz mailing list