add option "--instance", store axes_location in subset_plan and drop all
variation tables when all axes are pinned at default
diff --git a/src/gen-def.py b/src/gen-def.py
index 205ed7e..47b7b47 100755
--- a/src/gen-def.py
+++ b/src/gen-def.py
@@ -19,7 +19,9 @@
 if '--experimental-api' not in sys.argv:
 	# Move these to harfbuzz-sections.txt when got stable
 	experimental_symbols = \
-"""hb_subset_repack_or_fail""".splitlines ()
+"""hb_subset_repack_or_fail
+hb_subset_input_pin_axis_location
+hb_subset_input_pin_axis_to_default""".splitlines ()
 	symbols = [x for x in symbols if x not in experimental_symbols]
 symbols = "\n".join (symbols)
 
diff --git a/src/hb-map.hh b/src/hb-map.hh
index df215fc..71d45c1 100644
--- a/src/hb-map.hh
+++ b/src/hb-map.hh
@@ -455,4 +455,28 @@
   hb_map_t (const Iterable &o) : hashmap (o) {}
 };
 
+template <typename K, typename V>
+static inline
+hb_hashmap_t<K, V>* hb_hashmap_create ()
+{
+  using hashmap = hb_hashmap_t<K, V>;
+  hashmap* map;
+  if (!(map = hb_object_create<hashmap> ()))
+    return nullptr;
+
+  map->init_shallow ();
+
+  return map;
+}
+
+template <typename K, typename V>
+static inline
+void hb_hashmap_destroy (hb_hashmap_t<K, V>* map)
+{
+  if (!hb_object_destroy (map))
+    return;
+  map->fini_shallow ();
+  hb_free (map);
+}
+
 #endif /* HB_MAP_HH */
diff --git a/src/hb-ot-var-avar-table.hh b/src/hb-ot-var-avar-table.hh
index 65f26c1..71d767e 100644
--- a/src/hb-ot-var-avar-table.hh
+++ b/src/hb-ot-var-avar-table.hh
@@ -106,6 +106,14 @@
 {
   static constexpr hb_tag_t tableTag = HB_OT_TAG_avar;
 
+  bool has_data () const { return version.to_int (); }
+
+  const SegmentMaps* get_segment_maps () const
+  { return &firstAxisSegmentMaps; }
+
+  unsigned get_axis_count () const
+  { return axisCount; }
+
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
diff --git a/src/hb-ot-var-fvar-table.hh b/src/hb-ot-var-fvar-table.hh
index c5c476b..6300a42 100644
--- a/src/hb-ot-var-fvar-table.hh
+++ b/src/hb-ot-var-fvar-table.hh
@@ -96,6 +96,8 @@
     info->reserved = 0;
   }
 
+  hb_tag_t get_axis_tag () const { return axisTag; }
+
   int normalize_axis_value (float v) const
   {
     float min_value, default_value, max_value;
diff --git a/src/hb-subset-input.cc b/src/hb-subset-input.cc
index 70c33f4..06f9f98 100644
--- a/src/hb-subset-input.cc
+++ b/src/hb-subset-input.cc
@@ -48,7 +48,10 @@
   for (auto& set : input->sets_iter ())
     set = hb_set_create ();
 
-  if (input->in_error ())
+  if ((input->axes_location = hb_object_create<hb_hashmap_t<hb_tag_t, float>> ()))
+    input->axes_location->init_shallow ();
+  
+  if (!input->axes_location || input->in_error ())
   {
     hb_subset_input_destroy (input);
     return nullptr;
@@ -246,6 +249,13 @@
   for (hb_set_t* set : input->sets_iter ())
     hb_set_destroy (set);
 
+  if (input->axes_location)
+  {
+    hb_object_destroy (input->axes_location);
+    input->axes_location->fini_shallow ();
+    hb_free (input->axes_location);
+  }
+
   hb_free (input);
 }
 
@@ -376,3 +386,56 @@
 {
   return hb_object_get_user_data (input, key);
 }
+
+#ifdef HB_EXPERIMENTAL_API
+#ifndef HB_NO_VAR
+/**
+ * hb_subset_input_pin_axis_to_default: (skip)
+ * @input: a #hb_subset_input_t object.
+ * @axis_tag: Tag of the axis to be pinned
+ *
+ * Pin an axis to its default location in the given subset input object.
+ *
+ * Return value: `true` if success, `false` otherwise
+ *
+ * Since: REPLACEME
+ **/
+hb_bool_t
+hb_subset_input_pin_axis_to_default (hb_subset_input_t  *input,
+                                     hb_face_t          *face,
+                                     hb_tag_t            axis_tag)
+{
+  hb_ot_var_axis_info_t axis_info;
+  if (!hb_ot_var_find_axis_info (face, axis_tag, &axis_info))
+    return false;
+
+  return input->axes_location->set (axis_tag, axis_info.default_value);
+}
+
+/**
+ * hb_subset_input_pin_axis_location: (skip)
+ * @input: a #hb_subset_input_t object.
+ * @axis_tag: Tag of the axis to be pinned
+ * @axis_value: Location on the axis to be pinned at
+ *
+ * Pin an axis to a fixed location in the given subset input object.
+ *
+ * Return value: `true` if success, `false` otherwise
+ *
+ * Since: REPLACEME
+ **/
+hb_bool_t
+hb_subset_input_pin_axis_location (hb_subset_input_t  *input,
+                                   hb_face_t          *face,
+                                   hb_tag_t            axis_tag,
+                                   float               axis_value)
+{
+  hb_ot_var_axis_info_t axis_info;
+  if (!hb_ot_var_find_axis_info (face, axis_tag, &axis_info))
+    return false;
+
+  float val = hb_clamp(axis_value, axis_info.min_value, axis_info.max_value);
+  return input->axes_location->set (axis_tag, val);
+}
+#endif
+#endif
diff --git a/src/hb-subset-input.hh b/src/hb-subset-input.hh
index a21a6f3..2335f06 100644
--- a/src/hb-subset-input.hh
+++ b/src/hb-subset-input.hh
@@ -59,6 +59,7 @@
   };
 
   unsigned flags;
+  hb_hashmap_t<hb_tag_t, float> *axes_location;
 
   inline unsigned num_sets () const
   {
@@ -77,7 +78,8 @@
       if (unlikely (set_ptrs[i]->in_error ()))
         return true;
     }
-    return false;
+
+    return axes_location->in_error ();
   }
 };
 
diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc
index 8bda4b5..e405a46 100644
--- a/src/hb-subset-plan.cc
+++ b/src/hb-subset-plan.cc
@@ -37,6 +37,7 @@
 #include "hb-ot-color-colr-table.hh"
 #include "hb-ot-color-colrv1-closure.hh"
 #include "hb-ot-var-fvar-table.hh"
+#include "hb-ot-var-avar-table.hh"
 #include "hb-ot-stat-table.hh"
 #include "hb-ot-math-table.hh"
 
@@ -585,6 +586,48 @@
 #endif
 }
 
+
+static void
+_normalize_axes_location (hb_face_t *face,
+			  const hb_hashmap_t<hb_tag_t, float> *user_axes_location,
+			  hb_hashmap_t<hb_tag_t, int> *normalized_axes_location, /* OUT */
+			  bool &all_axes_pinned)
+{
+  if (user_axes_location->is_empty ())
+    return;
+
+  hb_array_t<const OT::AxisRecord> axes = face->table.fvar->get_axes ();
+
+  bool has_avar = face->table.avar->has_data ();
+  const OT::SegmentMaps *seg_maps = nullptr;
+  if (has_avar)
+    seg_maps = face->table.avar->get_segment_maps ();
+
+  bool axis_not_pinned = false;
+  unsigned axis_count = 0;
+  for (const auto& axis : axes)
+  {
+    hb_tag_t axis_tag = axis.get_axis_tag ();
+    if (!user_axes_location->has (axis_tag))
+    {
+      axis_not_pinned = true;
+    }
+    else
+    {
+      int normalized_v = axis.normalize_axis_value (user_axes_location->get (axis_tag));
+      if (has_avar && axis_count < face->table.avar->get_axis_count ())
+      {
+        normalized_v = seg_maps->map (normalized_v);
+      }
+      normalized_axes_location->set (axis_tag, normalized_v);
+    }
+    if (has_avar)
+      seg_maps = &StructAfter<OT::SegmentMaps> (*seg_maps);
+    
+    axis_count++;
+  }
+  all_axes_pinned = !axis_not_pinned;
+}
 /**
  * hb_subset_plan_create_or_fail:
  * @face: font face to create the plan for.
@@ -636,10 +679,8 @@
   plan->gsub_lookups = hb_map_create ();
   plan->gpos_lookups = hb_map_create ();
 
-  if (plan->check_success (plan->gsub_langsys = hb_object_create<script_langsys_map> ()))
-    plan->gsub_langsys->init_shallow ();
-  if (plan->check_success (plan->gpos_langsys = hb_object_create<script_langsys_map> ()))
-    plan->gpos_langsys->init_shallow ();
+  plan->check_success (plan->gsub_langsys = hb_hashmap_create<unsigned, hb::unique_ptr<hb_set_t>> ());
+  plan->check_success (plan->gpos_langsys = hb_hashmap_create<unsigned, hb::unique_ptr<hb_set_t>> ());
 
   plan->gsub_features = hb_map_create ();
   plan->gpos_features = hb_map_create ();
@@ -648,10 +689,9 @@
   plan->layout_variation_indices = hb_set_create ();
   plan->layout_variation_idx_map = hb_map_create ();
 
-
-  if ((plan->sanitized_table_cache = hb_object_create<hb_hashmap_t<hb_tag_t, hb::unique_ptr<hb_blob_t>>> ())) {
-    plan->sanitized_table_cache->init_shallow ();
-  }
+  plan->check_success (plan->sanitized_table_cache = hb_hashmap_create<hb_tag_t, hb::unique_ptr<hb_blob_t>> ());
+  plan->check_success (plan->axes_location = hb_hashmap_create<hb_tag_t, int> ());
+  plan->all_axes_pinned = false;
 
   if (unlikely (plan->in_error ())) {
     hb_subset_plan_destroy (plan);
@@ -685,6 +725,11 @@
         plan->glyph_map->get(plan->unicode_to_new_gid_list.arrayZ[i].second);
   }
 
+  _normalize_axes_location (face,
+                            input->axes_location,
+                            plan->axes_location,
+                            plan->all_axes_pinned);
+
   if (unlikely (plan->in_error ())) {
     hb_subset_plan_destroy (plan);
     return nullptr;
@@ -734,25 +779,10 @@
   hb_set_destroy (plan->layout_variation_indices);
   hb_map_destroy (plan->layout_variation_idx_map);
 
-  if (plan->gsub_langsys)
-  {
-    hb_object_destroy (plan->gsub_langsys);
-    plan->gsub_langsys->fini_shallow ();
-    hb_free (plan->gsub_langsys);
-  }
-
-  if (plan->gpos_langsys)
-  {
-    hb_object_destroy (plan->gpos_langsys);
-    plan->gpos_langsys->fini_shallow ();
-    hb_free (plan->gpos_langsys);
-  }
-
-  if (plan->sanitized_table_cache) {
-    hb_object_destroy (plan->sanitized_table_cache);
-    plan->sanitized_table_cache->fini ();
-    hb_free (plan->sanitized_table_cache);
-  }
+  hb_hashmap_destroy (plan->gsub_langsys);
+  hb_hashmap_destroy (plan->gpos_langsys);
+  hb_hashmap_destroy (plan->axes_location);
+  hb_hashmap_destroy (plan->sanitized_table_cache);
 
   hb_free (plan);
 }
diff --git a/src/hb-subset-plan.hh b/src/hb-subset-plan.hh
index c631ea0..49edacc 100644
--- a/src/hb-subset-plan.hh
+++ b/src/hb-subset-plan.hh
@@ -107,6 +107,9 @@
   hb_map_t *layout_variation_idx_map;
 
   hb_hashmap_t<hb_tag_t, hb::unique_ptr<hb_blob_t>>* sanitized_table_cache;
+  //normalized axes location map
+  hb_hashmap_t<hb_tag_t, int> *axes_location;
+  bool all_axes_pinned;
 
  public:
 
diff --git a/src/hb-subset.cc b/src/hb-subset.cc
index 34ef923..bcbe73d 100644
--- a/src/hb-subset.cc
+++ b/src/hb-subset.cc
@@ -361,6 +361,8 @@
   switch (tag)
   {
   case HB_TAG ('c','v','a','r'): /* hint table, fallthrough */
+    return plan->all_axes_pinned || (plan->flags & HB_SUBSET_FLAGS_NO_HINTING);
+
   case HB_TAG ('c','v','t',' '): /* hint table, fallthrough */
   case HB_TAG ('f','p','g','m'): /* hint table, fallthrough */
   case HB_TAG ('p','r','e','p'): /* hint table, fallthrough */
@@ -380,6 +382,14 @@
     return true;
 #endif
 
+  case HB_TAG ('a','v','a','r'):
+  case HB_TAG ('f','v','a','r'):
+  case HB_TAG ('g','v','a','r'):
+  case HB_OT_TAG_HVAR:
+  case HB_OT_TAG_VVAR:
+  case HB_TAG ('M','V','A','R'):
+    return plan->all_axes_pinned;
+
   default:
     return false;
   }
diff --git a/src/hb-subset.h b/src/hb-subset.h
index d0064cb..f4346c7 100644
--- a/src/hb-subset.h
+++ b/src/hb-subset.h
@@ -154,6 +154,21 @@
 hb_subset_input_set_flags (hb_subset_input_t *input,
 			   unsigned value);
 
+#ifdef HB_EXPERIMENTAL_API
+#ifndef HB_NO_VAR
+HB_EXTERN hb_bool_t
+hb_subset_input_pin_axis_to_default (hb_subset_input_t  *input,
+				     hb_face_t          *face,
+				     hb_tag_t            axis_tag);
+
+HB_EXTERN hb_bool_t
+hb_subset_input_pin_axis_location (hb_subset_input_t  *input,
+				   hb_face_t          *face,
+				   hb_tag_t            axis_tag,
+				   float               axis_value);
+#endif
+#endif
+
 HB_EXTERN hb_face_t *
 hb_subset_or_fail (hb_face_t *source, const hb_subset_input_t *input);
 
diff --git a/util/hb-subset.cc b/util/hb-subset.cc
index 0592bfa..bca7140 100644
--- a/util/hb-subset.cc
+++ b/util/hb-subset.cc
@@ -648,6 +648,73 @@
   return true;
 }
 
+#ifdef HB_EXPERIMENTAL_API
+#ifndef HB_NO_VAR
+static gboolean
+parse_instance (const char *name,
+		const char *arg,
+		gpointer    data,
+		GError    **error)
+{
+  subset_main_t *subset_main = (subset_main_t *) data;
+  
+  char *s = strtok((char *) arg, "=");
+  while (s)
+  {
+    unsigned len = strlen (s);
+    if (len > 4)  //Axis tags are 4 bytes.
+    {
+      g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
+		   "Failed parsing axis tag at: '%s'", s);
+      return false;
+    }
+
+    hb_tag_t axis_tag = hb_tag_from_string (s, len);
+
+    s = strtok(nullptr, ", ");
+    if (!s)
+    {
+      g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
+		   "Value not specified for axis: %c%c%c%c", HB_UNTAG (axis_tag));
+      return false;
+    }
+
+    if (strcmp (s, "drop") == 0)
+    {
+      if (!hb_subset_input_pin_axis_to_default (subset_main->input, subset_main->face, axis_tag))
+      {
+        g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
+                     "Cannot pin axis: '%c%c%c%c', not present in fvar", HB_UNTAG (axis_tag));
+        return false;
+      }
+    }
+    else
+    {
+      errno = 0;
+      char *p;
+      float axis_value = strtof (s, &p);
+      if (errno || s == p)
+      {
+        g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
+                     "Failed parsing axis value at: '%s'", s);
+        return false;
+      }
+
+      if (!hb_subset_input_pin_axis_location (subset_main->input, subset_main->face, axis_tag, axis_value))
+      {
+        g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
+                     "Cannot pin axis: '%c%c%c%c', not present in fvar", HB_UNTAG (axis_tag));
+        return false;
+      }
+    }
+    s = strtok(nullptr, "=");
+  }
+
+  return true;
+}
+#endif
+#endif
+
 template <GOptionArgFunc line_parser, bool allow_comments=true>
 static gboolean
 parse_file_for (const char *name,
@@ -818,7 +885,18 @@
     {"drop-tables",	0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_drop_tables,	"Drop the specified tables. Use --drop-tables-=... to subtract from the current set.", "list of string table tags or *"},
     {"drop-tables+",	0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_drop_tables,	"Drop the specified tables.", "list of string table tags or *"},
     {"drop-tables-",	0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_drop_tables,	"Drop the specified tables.", "list of string table tags or *"},
+#ifdef HB_EXPERIMENTAL_API
+#ifndef HB_NO_VAR
+    {"instance",	0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_instance,
+     "(Partially|Fully) Instantiate a variable font. A location consists of the tag of a variation axis, followed by '=', followed by a\n"
+     "number or the literal string 'drop'\n"
+     "                                                       "
+     "For example: --instance=\"wdth=100 wght=200\" or --instance=\"wdth=drop\"\n"
+     "Note: currently only fully instancing to the default location is supported\n",
+     "list of comma separated axis-locations"},
     {nullptr}
+#endif
+#endif
   };
   add_group (other_entries,
 	     "subset-other",