[subset] closure lookups for GSUB/GPOS
diff --git a/docs/harfbuzz-sections.txt b/docs/harfbuzz-sections.txt
index 93d6de6..1cf731c 100644
--- a/docs/harfbuzz-sections.txt
+++ b/docs/harfbuzz-sections.txt
@@ -551,6 +551,7 @@
 HB_OT_TAG_GSUB
 HB_OT_TAG_JSTF
 hb_ot_layout_baseline_tag_t
+hb_ot_layout_closure_lookups
 hb_ot_layout_collect_lookups
 hb_ot_layout_collect_features
 hb_ot_layout_feature_get_characters
diff --git a/src/hb-ot-layout-common.hh b/src/hb-ot-layout-common.hh
index 540783d..c85c5a3 100644
--- a/src/hb-ot-layout-common.hh
+++ b/src/hb-ot-layout-common.hh
@@ -2299,6 +2299,11 @@
 {
   friend struct FeatureTableSubstitution;
 
+  void collect_lookups (hb_set_t *lookup_indexes /* OUT */) const
+  {
+    return (this+feature).add_lookup_indexes_to (lookup_indexes);
+  }
+
   bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
@@ -2326,6 +2331,18 @@
     return nullptr;
   }
 
+  void collect_lookups (const hb_set_t *feature_indexes,
+			hb_set_t       *lookup_indexes /* OUT */) const
+  {
+    + hb_iter (substitutions)
+    | hb_filter (feature_indexes, &FeatureTableSubstitutionRecord::featureIndex)
+    | hb_apply ([=] (const FeatureTableSubstitutionRecord& r)
+                {
+                  r.collect_lookups (lookup_indexes);
+                })
+    ;
+  }
+
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -2346,6 +2363,12 @@
 {
   friend struct FeatureVariations;
 
+  void collect_lookups (const hb_set_t *feature_indexes,
+			hb_set_t       *lookup_indexes /* OUT */) const
+  {
+    return (this+substitutions).collect_lookups (feature_indexes, lookup_indexes);
+  }
+
   bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
@@ -2396,6 +2419,13 @@
     return_trace (c->embed (*this));
   }
 
+  void collect_lookups (const hb_set_t *feature_indexes,
+			hb_set_t       *lookup_indexes /* OUT */) const
+  {
+    for (const FeatureVariationRecord& r : varRecords)
+      r.collect_lookups (feature_indexes, lookup_indexes);
+  }
+
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh
index decf798..1c5d137 100644
--- a/src/hb-ot-layout-gpos-table.hh
+++ b/src/hb-ot-layout-gpos-table.hh
@@ -519,6 +519,8 @@
   bool intersects (const hb_set_t *glyphs) const
   { return (this+coverage).intersects (glyphs); }
 
+  void closure_lookups (hb_closure_lookups_context_t *c) const {}
+
   void collect_glyphs (hb_collect_glyphs_context_t *c) const
   { if (unlikely (!(this+coverage).add_coverage (c->input))) return; }
 
@@ -601,6 +603,8 @@
   bool intersects (const hb_set_t *glyphs) const
   { return (this+coverage).intersects (glyphs); }
 
+  void closure_lookups (hb_closure_lookups_context_t *c) const {}
+
   void collect_glyphs (hb_collect_glyphs_context_t *c) const
   { if (unlikely (!(this+coverage).add_coverage (c->input))) return; }
 
@@ -923,6 +927,8 @@
     ;
   }
 
+  void closure_lookups (hb_closure_lookups_context_t *c) const {}
+
   void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
@@ -1034,6 +1040,8 @@
 	   (this+classDef2).intersects (glyphs);
   }
 
+  void closure_lookups (hb_closure_lookups_context_t *c) const {}
+
   void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
@@ -1239,6 +1247,8 @@
   bool intersects (const hb_set_t *glyphs) const
   { return (this+coverage).intersects (glyphs); }
 
+  void closure_lookups (hb_closure_lookups_context_t *c) const {}
+
   void collect_glyphs (hb_collect_glyphs_context_t *c) const
   { if (unlikely (!(this+coverage).add_coverage (c->input))) return; }
 
@@ -1438,6 +1448,8 @@
   { return (this+markCoverage).intersects (glyphs) &&
 	   (this+baseCoverage).intersects (glyphs); }
 
+  void closure_lookups (hb_closure_lookups_context_t *c) const {}
+
   void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     if (unlikely (!(this+markCoverage).add_coverage (c->input))) return;
@@ -1559,6 +1571,8 @@
   { return (this+markCoverage).intersects (glyphs) &&
 	   (this+ligatureCoverage).intersects (glyphs); }
 
+  void closure_lookups (hb_closure_lookups_context_t *c) const {}
+
   void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     if (unlikely (!(this+markCoverage).add_coverage (c->input))) return;
@@ -1679,6 +1693,8 @@
   { return (this+mark1Coverage).intersects (glyphs) &&
 	   (this+mark2Coverage).intersects (glyphs); }
 
+  void closure_lookups (hb_closure_lookups_context_t *c) const {}
+
   void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     if (unlikely (!(this+mark1Coverage).add_coverage (c->input))) return;
@@ -1885,6 +1901,23 @@
   hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const
   { return dispatch (c); }
 
+  hb_closure_lookups_context_t::return_t closure_lookups (hb_closure_lookups_context_t *c, unsigned this_index) const
+  {
+    if (c->is_lookup_visited (this_index))
+      return hb_closure_lookups_context_t::default_return_value ();
+
+    c->set_lookup_visited (this_index);
+    if (!intersects (c->glyphs))
+    {
+      c->set_lookup_inactive (this_index);
+      return hb_closure_lookups_context_t::default_return_value ();
+    }
+    c->set_recurse_func (dispatch_closure_lookups_recurse_func);
+
+    hb_closure_lookups_context_t::return_t ret = dispatch (c);
+    return ret;
+  }
+
   template <typename set_t>
   void add_coverage (set_t *glyphs) const
   {
@@ -1897,6 +1930,8 @@
   template <typename context_t>
   static typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index);
 
+  HB_INTERNAL static hb_closure_lookups_context_t::return_t dispatch_closure_lookups_recurse_func (hb_closure_lookups_context_t *c, unsigned this_index);
+
   template <typename context_t, typename ...Ts>
   typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
   { return Lookup::dispatch<SubTable> (c, hb_forward<Ts> (ds)...); }
@@ -2053,6 +2088,13 @@
   const PosLookup &l = c->face->table.GPOS.get_relaxed ()->table->get_lookup (lookup_index);
   return l.dispatch (c);
 }
+
+/*static*/ inline hb_closure_lookups_context_t::return_t PosLookup::dispatch_closure_lookups_recurse_func (hb_closure_lookups_context_t *c, unsigned this_index)
+{
+  const PosLookup &l = c->face->table.GPOS.get_relaxed ()->table->get_lookup (this_index);
+  return l.closure_lookups (c, this_index);
+}
+
 /*static*/ bool PosLookup::apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index)
 {
   const PosLookup &l = c->face->table.GPOS.get_relaxed ()->table->get_lookup (lookup_index);
diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh
index 7cb5889..5c6768f 100644
--- a/src/hb-ot-layout-gsub-table.hh
+++ b/src/hb-ot-layout-gsub-table.hh
@@ -56,6 +56,8 @@
     ;
   }
 
+  void closure_lookups (hb_closure_lookups_context_t *c) const {}
+
   void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
@@ -154,6 +156,8 @@
     ;
   }
 
+  void closure_lookups (hb_closure_lookups_context_t *c) const {}
+
   void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
@@ -395,6 +399,8 @@
     ;
   }
 
+  void closure_lookups (hb_closure_lookups_context_t *c) const {}
+
   void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
@@ -605,6 +611,8 @@
     ;
   }
 
+  void closure_lookups (hb_closure_lookups_context_t *c) const {}
+
   void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
@@ -966,6 +974,8 @@
     ;
   }
 
+  void closure_lookups (hb_closure_lookups_context_t *c) const {}
+
   void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
@@ -1156,6 +1166,8 @@
     ;
   }
 
+  void closure_lookups (hb_closure_lookups_context_t *c) const {}
+
   void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
@@ -1372,6 +1384,24 @@
     return ret;
   }
 
+  hb_closure_lookups_context_t::return_t closure_lookups (hb_closure_lookups_context_t *c, unsigned this_index) const
+  {
+    if (c->is_lookup_visited (this_index))
+      return hb_closure_lookups_context_t::default_return_value ();
+
+    c->set_lookup_visited (this_index);
+    if (!intersects (c->glyphs))
+    {
+      c->set_lookup_inactive (this_index);
+      return hb_closure_lookups_context_t::default_return_value ();
+    }
+
+    c->set_recurse_func (dispatch_closure_lookups_recurse_func);
+
+    hb_closure_lookups_context_t::return_t ret = dispatch (c);
+    return ret;
+  }
+
   hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     c->set_recurse_func (dispatch_recurse_func<hb_collect_glyphs_context_t>);
@@ -1477,6 +1507,8 @@
     return ret;
   }
 
+  HB_INTERNAL static hb_closure_lookups_context_t::return_t dispatch_closure_lookups_recurse_func (hb_closure_lookups_context_t *c, unsigned lookup_index);
+
   template <typename context_t, typename ...Ts>
   typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
   { return Lookup::dispatch<SubTable> (c, hb_forward<Ts> (ds)...); }
@@ -1529,6 +1561,13 @@
   const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (lookup_index);
   return l.dispatch (c);
 }
+
+/*static*/ inline hb_closure_lookups_context_t::return_t SubstLookup::dispatch_closure_lookups_recurse_func (hb_closure_lookups_context_t *c, unsigned this_index)
+{
+  const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (this_index);
+  return l.closure_lookups (c, this_index);
+}
+
 /*static*/ bool SubstLookup::apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index)
 {
   const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (lookup_index);
diff --git a/src/hb-ot-layout-gsubgpos.hh b/src/hb-ot-layout-gsubgpos.hh
index 785fe20..78f2bf3 100644
--- a/src/hb-ot-layout-gsubgpos.hh
+++ b/src/hb-ot-layout-gsubgpos.hh
@@ -122,6 +122,63 @@
   hb_map_t *done_lookups;
 };
 
+struct hb_closure_lookups_context_t :
+       hb_dispatch_context_t<hb_closure_lookups_context_t, hb_empty_t, 0>
+{
+  const char *get_name () { return "CLOSURE_LOOKUPS"; }
+  typedef return_t (*recurse_func_t) (hb_closure_lookups_context_t *c, unsigned lookup_index);
+  template <typename T>
+  return_t dispatch (const T &obj) { obj.closure_lookups (this); return hb_empty_t (); }
+  static return_t default_return_value () { return hb_empty_t (); }
+  void recurse (unsigned lookup_index)
+  {
+    if (unlikely (nesting_level_left == 0 || !recurse_func))
+      return;
+
+    /* Return if new lookup was recursed to before. */
+    if (is_lookup_visited (lookup_index))
+      return;
+
+    set_lookup_visited (lookup_index);
+    nesting_level_left--;
+    recurse_func (this, lookup_index);
+    nesting_level_left++;
+  }
+
+  void set_lookup_visited (unsigned lookup_index)
+  { visited_lookups->add (lookup_index); }
+
+  void set_lookup_inactive (unsigned lookup_index)
+  { inactive_lookups->add (lookup_index); }
+
+  bool is_lookup_visited (unsigned lookup_index)
+  { return visited_lookups->has (lookup_index); }
+
+  hb_face_t *face;
+  const hb_set_t *glyphs;
+  recurse_func_t recurse_func;
+  unsigned int nesting_level_left;
+  unsigned int debug_depth;
+
+  hb_closure_lookups_context_t (hb_face_t *face_,
+				const hb_set_t *glyphs_,
+				hb_set_t *visited_lookups_,
+				hb_set_t *inactive_lookups_,
+				unsigned nesting_level_left_ = HB_MAX_NESTING_LEVEL) :
+				face (face_),
+				glyphs (glyphs_),
+				recurse_func (nullptr),
+				nesting_level_left (nesting_level_left_),
+				debug_depth (0),
+				visited_lookups (visited_lookups_),
+				inactive_lookups (inactive_lookups_) {}
+
+  void set_recurse_func (recurse_func_t func) { recurse_func = func; }
+
+  private:
+  hb_set_t *visited_lookups;
+  hb_set_t *inactive_lookups;
+};
 
 struct hb_would_apply_context_t :
        hb_dispatch_context_t<hb_would_apply_context_t, bool, 0>
@@ -1061,6 +1118,17 @@
 
 struct LookupRecord
 {
+  LookupRecord* copy (hb_serialize_context_t *c,
+		      const hb_map_t         *lookup_map)
+  {
+    TRACE_SERIALIZE (this);
+    auto *out = c->embed (*this);
+    if (unlikely (!out)) return_trace (nullptr);
+
+    out->lookupListIndex = hb_map_get (lookup_map, lookupListIndex);
+    return_trace (out);
+  }
+
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -1315,6 +1383,13 @@
 			    lookup_context);
   }
 
+  void closure_lookups (hb_closure_lookups_context_t *c) const
+  {
+    const UnsizedArrayOf<LookupRecord> &lookupRecord = StructAfter<UnsizedArrayOf<LookupRecord>>
+						       (inputZ.as_array (inputCount ? inputCount - 1 : 0));
+    recurse_lookups (c, lookupCount, lookupRecord.arrayZ);
+  }
+
   void collect_glyphs (hb_collect_glyphs_context_t *c,
 		       ContextCollectGlyphsLookupContext &lookup_context) const
   {
@@ -1395,6 +1470,15 @@
     ;
   }
 
+  void closure_lookups (hb_closure_lookups_context_t *c) const
+  {
+    return
+    + hb_iter (rule)
+    | hb_map (hb_add (this))
+    | hb_apply ([&] (const Rule &_) { _.closure_lookups (c); })
+    ;
+  }
+
   void collect_glyphs (hb_collect_glyphs_context_t *c,
 		       ContextCollectGlyphsLookupContext &lookup_context) const
   {
@@ -1478,6 +1562,14 @@
     ;
   }
 
+  void closure_lookups (hb_closure_lookups_context_t *c) const
+  {
+    + hb_iter (ruleSet)
+    | hb_map (hb_add (this))
+    | hb_apply ([&] (const RuleSet &_) { _.closure_lookups (c); })
+    ;
+  }
+
   void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     (this+coverage).add_coverage (c->input);
@@ -1592,6 +1684,14 @@
     ;
   }
 
+  void closure_lookups (hb_closure_lookups_context_t *c) const
+  {
+    + hb_iter (ruleSet)
+    | hb_map (hb_add (this))
+    | hb_apply ([&] (const RuleSet &_) { _.closure_lookups (c); })
+    ;
+  }
+
   void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     (this+coverage).add_coverage (c->input);
@@ -1699,6 +1799,12 @@
 			    lookup_context);
   }
 
+  void closure_lookups (hb_closure_lookups_context_t *c) const
+  {
+    const LookupRecord *lookupRecord = &StructAfter<LookupRecord> (coverageZ.as_array (glyphCount));
+    recurse_lookups (c, lookupCount, lookupRecord);
+  }
+
   void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     (this+coverageZ[0]).add_coverage (c->input);
@@ -1964,6 +2070,14 @@
 				  lookup_context);
   }
 
+  void closure_lookups (hb_closure_lookups_context_t *c) const
+  {
+    const HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16>> (backtrack);
+    const ArrayOf<HBUINT16> &lookahead = StructAfter<ArrayOf<HBUINT16>> (input);
+    const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord>> (lookahead);
+    recurse_lookups (c, lookup.len, lookup.arrayZ);
+  }
+
   void collect_glyphs (hb_collect_glyphs_context_t *c,
 		       ChainContextCollectGlyphsLookupContext &lookup_context) const
   {
@@ -2131,6 +2245,15 @@
     ;
   }
 
+  void closure_lookups (hb_closure_lookups_context_t *c) const
+  {
+    return
+    + hb_iter (rule)
+    | hb_map (hb_add (this))
+    | hb_apply ([&] (const ChainRule &_) { _.closure_lookups (c); })
+    ;
+  }
+
   void collect_glyphs (hb_collect_glyphs_context_t *c, ChainContextCollectGlyphsLookupContext &lookup_context) const
   {
     return
@@ -2244,6 +2367,14 @@
     ;
   }
 
+  void closure_lookups (hb_closure_lookups_context_t *c) const
+  {
+    + hb_iter (ruleSet)
+    | hb_map (hb_add (this))
+    | hb_apply ([&] (const ChainRuleSet &_) { _.closure_lookups (c); })
+    ;
+  }
+
   void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     (this+coverage).add_coverage (c->input);
@@ -2380,6 +2511,14 @@
     ;
   }
 
+  void closure_lookups (hb_closure_lookups_context_t *c) const
+  {
+    + hb_iter (ruleSet)
+    | hb_map (hb_add (this))
+    | hb_apply ([&] (const ChainRuleSet &_) { _.closure_lookups (c); })
+    ;
+  }
+
   void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     (this+coverage).add_coverage (c->input);
@@ -2570,6 +2709,14 @@
 				  lookup_context);
   }
 
+  void closure_lookups (hb_closure_lookups_context_t *c) const
+  {
+    const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
+    const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (input);
+    const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord>> (lookahead);
+    recurse_lookups (c, lookup.len, lookup.arrayZ);
+  }
+
   void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
@@ -2879,7 +3026,7 @@
   bool find_variations_index (const int *coords, unsigned int num_coords,
 			      unsigned int *index) const
   {
-#ifdef HB_NOVAR
+#ifdef HB_NO_VAR
     return false;
 #endif
     return (version.to_int () >= 0x00010001u ? this+featureVars : Null(FeatureVariations))
@@ -2901,6 +3048,15 @@
     return get_feature (feature_index);
   }
 
+  void feature_variation_collect_lookups (const hb_set_t *feature_indexes,
+					  hb_set_t       *lookup_indexes /* OUT */) const
+  {
+#ifndef HB_NO_VAR
+    if (version.to_int () >= 0x00010001u)
+      (this+featureVars).collect_lookups (feature_indexes, lookup_indexes);
+#endif
+  }
+
   template <typename TLookup>
   bool subset (hb_subset_context_t *c) const
   {
diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index cfc41df..0a7cf4f 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -1191,6 +1191,50 @@
   for (hb_codepoint_t feature_index = HB_SET_VALUE_INVALID;
        hb_set_next (&feature_indexes, &feature_index);)
     g.get_feature (feature_index).add_lookup_indexes_to (lookup_indexes);
+
+  g.feature_variation_collect_lookups (&feature_indexes, lookup_indexes);
+}
+
+/**
+ * hb_ot_layout_closure_lookups:
+ * @face: #hb_face_t to work upon
+ * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
+ * @lookup_indexes: (inout): lookup_indices collected from feature
+ * list
+ *
+ * Returns all inactive lookups reachable from lookup_indices
+ * Since: 2.6.4
+ **/
+void
+hb_ot_layout_closure_lookups (hb_face_t      *face,
+			      hb_tag_t        table_tag,
+			      const hb_set_t *glyphs,
+			      hb_set_t       *lookup_indexes /* IN/OUT */)
+{
+  hb_set_t visited_lookups, inactive_lookups;
+  OT::hb_closure_lookups_context_t c (face, glyphs, &visited_lookups, &inactive_lookups);
+
+  for (unsigned lookup_index : + hb_iter (lookup_indexes))
+  {
+    switch (table_tag)
+    {
+      case HB_OT_TAG_GSUB:
+      {
+        const OT::SubstLookup& l = face->table.GSUB->table->get_lookup (lookup_index);
+        l.closure_lookups (&c, lookup_index);
+        break;
+      }
+      case HB_OT_TAG_GPOS:
+      {
+        const OT::PosLookup& l = face->table.GPOS->table->get_lookup (lookup_index);
+        l.closure_lookups (&c, lookup_index);
+        break;
+      }
+    }
+  }
+
+  hb_set_union (lookup_indexes, &visited_lookups);
+  hb_set_subtract (lookup_indexes, &inactive_lookups);
 }
 
 
diff --git a/src/hb-ot-layout.h b/src/hb-ot-layout.h
index 7e8a897..3fc22b7 100644
--- a/src/hb-ot-layout.h
+++ b/src/hb-ot-layout.h
@@ -264,6 +264,12 @@
 			      hb_set_t       *lookup_indexes /* OUT */);
 
 HB_EXTERN void
+hb_ot_layout_closure_lookups (hb_face_t      *face,
+			      hb_tag_t        table_tag,
+			      const hb_set_t *glyphs,
+			      hb_set_t       *lookup_indexes /* IN/OUT */);
+
+HB_EXTERN void
 hb_ot_layout_lookup_collect_glyphs (hb_face_t    *face,
 				    hb_tag_t      table_tag,
 				    unsigned int  lookup_index,
diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc
index f61528c..063b283 100644
--- a/src/hb-subset-plan.cc
+++ b/src/hb-subset-plan.cc
@@ -50,8 +50,21 @@
 #endif
 
 #ifndef HB_NO_SUBSET_LAYOUT
+static void
+_remap_lookups (const hb_set_t *lookup_indices,
+		hb_map_t       *lookups /* OUT */)
+{
+  unsigned count = lookup_indices->get_population ();
+
+  for (auto _ : + hb_zip (lookup_indices->iter (), hb_range (count)))
+    lookups->set (_.first, _.second);
+
+}
+
 static inline void
-_gsub_closure (hb_face_t *face, hb_set_t *gids_to_retain)
+_gsub_closure_glyphs_and_lookups (hb_face_t *face,
+				  hb_set_t *gids_to_retain,
+				  hb_map_t *gsub_lookups)
 {
   hb_set_t lookup_indices;
   hb_ot_layout_collect_lookups (face,
@@ -63,6 +76,30 @@
   hb_ot_layout_lookups_substitute_closure (face,
 					   &lookup_indices,
 					   gids_to_retain);
+  hb_ot_layout_closure_lookups (face,
+				HB_OT_TAG_GSUB,
+				gids_to_retain,
+				&lookup_indices);
+  _remap_lookups (&lookup_indices, gsub_lookups);
+}
+
+static inline void
+_gpos_closure_lookups (hb_face_t      *face,
+		       const hb_set_t *gids_to_retain,
+		       hb_map_t       *gpos_lookups)
+{
+  hb_set_t lookup_indices;
+  hb_ot_layout_collect_lookups (face,
+				HB_OT_TAG_GPOS,
+				nullptr,
+				nullptr,
+				nullptr,
+				&lookup_indices);
+  hb_ot_layout_closure_lookups (face,
+				HB_OT_TAG_GPOS,
+				gids_to_retain,
+				&lookup_indices);
+  _remap_lookups (&lookup_indices, gpos_lookups);
 }
 #endif
 
@@ -93,7 +130,8 @@
 _populate_gids_to_retain (hb_subset_plan_t* plan,
 			  const hb_set_t *unicodes,
 			  const hb_set_t *input_glyphs_to_retain,
-			  bool close_over_gsub)
+			  bool close_over_gsub,
+			  bool close_over_gpos)
 {
   OT::cmap::accelerator_t cmap;
   OT::glyf::accelerator_t glyf;
@@ -127,8 +165,11 @@
 
 #ifndef HB_NO_SUBSET_LAYOUT
   if (close_over_gsub)
-    // Add all glyphs needed for GSUB substitutions.
-    _gsub_closure (plan->source, plan->_glyphset_gsub);
+    // closure all glyphs/lookups needed for GSUB substitutions.
+    _gsub_closure_glyphs_and_lookups (plan->source, plan->_glyphset_gsub, plan->gsub_lookups);
+
+  if (close_over_gpos)
+    _gpos_closure_lookups (plan->source, plan->_glyphset_gsub, plan->gpos_lookups);
 #endif
   _remove_invalid_gids (plan->_glyphset_gsub, plan->source->get_num_glyphs ());
 
@@ -231,11 +272,14 @@
   plan->codepoint_to_glyph = hb_map_create ();
   plan->glyph_map = hb_map_create ();
   plan->reverse_glyph_map = hb_map_create ();
+  plan->gsub_lookups = hb_map_create ();
+  plan->gpos_lookups = hb_map_create ();
 
   _populate_gids_to_retain (plan,
 			    input->unicodes,
 			    input->glyphs,
-			    !input->drop_tables->has (HB_OT_TAG_GSUB));
+			    !input->drop_tables->has (HB_OT_TAG_GSUB),
+			    !input->drop_tables->has (HB_OT_TAG_GPOS));
 
   _create_old_gid_to_new_gid_map (face,
 				  input->retain_gids,
@@ -267,6 +311,8 @@
   hb_map_destroy (plan->reverse_glyph_map);
   hb_set_destroy (plan->_glyphset);
   hb_set_destroy (plan->_glyphset_gsub);
+  hb_map_destroy (plan->gsub_lookups);
+  hb_map_destroy (plan->gpos_lookups);
 
   free (plan);
 }
diff --git a/src/hb-subset-plan.hh b/src/hb-subset-plan.hh
index af2337e..3c5b66b 100644
--- a/src/hb-subset-plan.hh
+++ b/src/hb-subset-plan.hh
@@ -67,6 +67,10 @@
   hb_set_t *_glyphset;
   hb_set_t *_glyphset_gsub;
 
+  //active lookups we'd like to retain
+  hb_map_t *gsub_lookups;
+  hb_map_t *gpos_lookups;
+
  public:
 
   /*
diff --git a/test/api/test-ot-face.c b/test/api/test-ot-face.c
index 44a9116..e1f774d 100644
--- a/test/api/test-ot-face.c
+++ b/test/api/test-ot-face.c
@@ -74,6 +74,11 @@
   hb_ot_color_has_png (face);
   hb_blob_destroy (hb_ot_color_glyph_reference_png (font, cp));
 
+  hb_set_t *lookup_indexes = hb_set_create ();
+  hb_set_add (lookup_indexes, 0);
+  hb_ot_layout_closure_lookups (face, HB_OT_TAG_GSUB, set, lookup_indexes);
+  hb_set_destroy (lookup_indexes);
+
   hb_ot_layout_get_baseline (font, HB_OT_LAYOUT_BASELINE_TAG_HANGING, HB_DIRECTION_RTL, HB_SCRIPT_HANGUL, HB_TAG_NONE, NULL);
 
   hb_ot_layout_has_glyph_classes (face);