Merge branch 'master' into cff-more-arrayof-fixes
diff --git a/NEWS b/NEWS
index 890c258..ef87dad 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,10 @@
+Overview of changes leading to 2.3.1
+Wednesday, January 30, 2019
+====================================
+- AAT bug fixes.
+- Misc internal housekeeping cleanup.
+
+
 Overview of changes leading to 2.3.0
 Thursday, December 20, 2018
 ====================================
diff --git a/configure.ac b/configure.ac
index 989de12..718f1d0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
 AC_PREREQ([2.64])
 AC_INIT([HarfBuzz],
-        [2.3.0],
+        [2.3.1],
         [https://github.com/harfbuzz/harfbuzz/issues/new],
         [harfbuzz],
         [http://harfbuzz.org/])
@@ -515,6 +515,11 @@
 
 AC_OUTPUT
 
+echo
+echo "C++ compiler version:"
+$CXX --version
+echo
+
 AC_MSG_NOTICE([
 
 Build configuration:
diff --git a/src/hb-ot-hdmx-table.hh b/src/hb-ot-hdmx-table.hh
index 95229c5..953ccab 100644
--- a/src/hb-ot-hdmx-table.hh
+++ b/src/hb-ot-hdmx-table.hh
@@ -57,16 +57,19 @@
     }
 
     unsigned int len () const
-    { return this->subset_plan->glyphs.length; }
+    { return this->subset_plan->num_output_glyphs (); }
 
-    const HBUINT8* operator [] (unsigned int i) const
+    const HBUINT8* operator [] (unsigned int new_gid) const
     {
-      if (unlikely (i >= len ())) return nullptr;
-      hb_codepoint_t gid = this->subset_plan->glyphs [i];
+      if (unlikely (new_gid >= len ())) return nullptr;
 
-      if (gid >= sizeDeviceRecord - DeviceRecord::min_size)
+      hb_codepoint_t old_gid;
+      if (!this->subset_plan->old_gid_for_new_gid (new_gid, &old_gid))
+        return &Null(HBUINT8);
+
+      if (old_gid >= sizeDeviceRecord - DeviceRecord::min_size)
         return nullptr;
-      return &(this->source_device_record->widthsZ[gid]);
+      return &(this->source_device_record->widthsZ[old_gid]);
     }
   };
 
@@ -140,7 +143,7 @@
 
     this->version.set (source_hdmx->version);
     this->numRecords.set (source_hdmx->numRecords);
-    this->sizeDeviceRecord.set (DeviceRecord::get_size (plan->glyphs.length));
+    this->sizeDeviceRecord.set (DeviceRecord::get_size (plan->num_output_glyphs ()));
 
     for (unsigned int i = 0; i < source_hdmx->numRecords; i++)
     {
@@ -156,7 +159,7 @@
 
   static size_t get_subsetted_size (const hdmx *source_hdmx, hb_subset_plan_t *plan)
   {
-    return min_size + source_hdmx->numRecords * DeviceRecord::get_size (plan->glyphs.length);
+    return min_size + source_hdmx->numRecords * DeviceRecord::get_size (plan->num_output_glyphs ());
   }
 
   bool subset (hb_subset_plan_t *plan) const
diff --git a/src/hb-ot-hmtx-table.hh b/src/hb-ot-hmtx-table.hh
index a95a56f..9ef1f57 100644
--- a/src/hb-ot-hmtx-table.hh
+++ b/src/hb-ot-hmtx-table.hh
@@ -66,7 +66,7 @@
 
 
   bool subset_update_header (hb_subset_plan_t *plan,
-				    unsigned int num_hmetrics) const
+                             unsigned int num_hmetrics) const
   {
     hb_blob_t *src_blob = hb_sanitize_context_t ().reference_table<H> (plan->source, H::tableTag);
     hb_blob_t *dest_blob = hb_blob_copy_writable_or_fail (src_blob);
@@ -93,75 +93,49 @@
 
     /* All the trailing glyphs with the same advance can use one LongMetric
      * and just keep LSB */
-    hb_vector_t<hb_codepoint_t> &gids = plan->glyphs;
-    unsigned int num_advances = gids.length;
-    unsigned int last_advance = _mtx.get_advance (gids[num_advances - 1]);
-    while (num_advances > 1 &&
-	   last_advance == _mtx.get_advance (gids[num_advances - 2]))
-    {
-      num_advances--;
-    }
+    unsigned int num_output_glyphs = plan->num_output_glyphs ();
+    unsigned int num_advances = _mtx.num_advances_for_subset (plan);
 
     /* alloc the new table */
     size_t dest_sz = num_advances * 4
-		  + (gids.length - num_advances) * 2;
+		  + (num_output_glyphs - num_advances) * 2;
     void *dest = (void *) malloc (dest_sz);
     if (unlikely (!dest))
     {
       return false;
     }
     DEBUG_MSG(SUBSET, nullptr, "%c%c%c%c in src has %d advances, %d lsbs", HB_UNTAG(T::tableTag), _mtx.num_advances, _mtx.num_metrics - _mtx.num_advances);
-    DEBUG_MSG(SUBSET, nullptr, "%c%c%c%c in dest has %d advances, %d lsbs, %u bytes", HB_UNTAG(T::tableTag), num_advances, gids.length - num_advances, (unsigned int) dest_sz);
+    DEBUG_MSG(SUBSET, nullptr, "%c%c%c%c in dest has %d advances, %d lsbs, %u bytes",
+              HB_UNTAG(T::tableTag), num_advances, num_output_glyphs - num_advances, (unsigned int) dest_sz);
 
-    const char *source_table = hb_blob_get_data (_mtx.table.get_blob (), nullptr);
     // Copy everything over
-    LongMetric * old_metrics = (LongMetric *) source_table;
-    FWORD *lsbs = (FWORD *) (old_metrics + _mtx.num_advances);
     char * dest_pos = (char *) dest;
 
     bool failed = false;
-    for (unsigned int i = 0; i < gids.length; i++)
+    for (unsigned int i = 0; i < num_output_glyphs; i++)
     {
-      /* the last metric or the one for gids[i] */
-      LongMetric *src_metric = old_metrics + MIN ((hb_codepoint_t) _mtx.num_advances - 1, gids[i]);
-      if (gids[i] < _mtx.num_advances)
+      unsigned int side_bearing = 0;
+      unsigned int advance = 0;
+      hb_codepoint_t old_gid;
+      if (plan->old_gid_for_new_gid (i, &old_gid))
       {
-        /* src is a LongMetric */
-        if (i < num_advances)
-        {
-          /* dest is a LongMetric, copy it */
-          *((LongMetric *) dest_pos) = *src_metric;
-        }
-        else
-        {
-          /* dest just sb */
-          *((FWORD *) dest_pos) = src_metric->sb;
-        }
+        // Glyph is not an empty glyph so copy advance and side bearing
+        // from the input font.
+        side_bearing = _mtx.get_side_bearing (old_gid);
+        advance = _mtx.get_advance (old_gid);
+      }
+
+      bool has_advance = i < num_advances;
+      if (has_advance)
+      {
+        ((LongMetric *) dest_pos)->advance.set (advance);
+        ((LongMetric *) dest_pos)->sb.set (side_bearing);
       }
       else
       {
-	if (gids[i] >= _mtx.num_metrics)
-	{
-	  DEBUG_MSG(SUBSET, nullptr, "gid %d is >= number of source metrics %d",
-		    gids[i], _mtx.num_metrics);
-	  failed = true;
-	  break;
-	}
-	FWORD src_sb = *(lsbs + gids[i] - _mtx.num_advances);
-        if (i < num_advances)
-        {
-          /* dest needs a full LongMetric */
-          LongMetric *metric = (LongMetric *)dest_pos;
-          metric->advance = src_metric->advance;
-          metric->sb = src_sb;
-        }
-        else
-        {
-          /* dest just needs an sb */
-          *((FWORD *) dest_pos) = src_sb;
-        }
+        ((FWORD *) dest_pos)->set (side_bearing);
       }
-      dest_pos += (i < num_advances ? 4 : 2);
+      dest_pos += (has_advance ? 4 : 2);
     }
     _mtx.fini ();
 
@@ -187,7 +161,7 @@
     friend struct hmtxvmtx;
 
     void init (hb_face_t *face,
-		      unsigned int default_advance_ = 0)
+               unsigned int default_advance_ = 0)
     {
       default_advance = default_advance_ ? default_advance_ : hb_face_get_upem (face);
 
@@ -280,6 +254,32 @@
       return advance;
     }
 
+    unsigned int num_advances_for_subset (const hb_subset_plan_t *plan) const
+    {
+      unsigned int num_advances = plan->num_output_glyphs ();
+      unsigned int last_advance = _advance_for_new_gid (plan,
+                                                        num_advances - 1);
+      while (num_advances > 1 &&
+             last_advance == _advance_for_new_gid (plan,
+                                                   num_advances - 2))
+      {
+        num_advances--;
+      }
+
+      return num_advances;
+    }
+
+    private:
+    unsigned int _advance_for_new_gid (const hb_subset_plan_t *plan,
+                                       hb_codepoint_t new_gid) const
+    {
+      hb_codepoint_t old_gid;
+      if (!plan->old_gid_for_new_gid (new_gid, &old_gid))
+        return 0;
+
+      return get_advance (old_gid);
+    }
+
     public:
     bool has_font_extents;
     int ascender;
diff --git a/src/hb-ot-layout-common.hh b/src/hb-ot-layout-common.hh
index 39a8bba..9b17225 100644
--- a/src/hb-ot-layout-common.hh
+++ b/src/hb-ot-layout-common.hh
@@ -1222,7 +1222,7 @@
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->glyphset;
+    const hb_set_t &glyphset = *c->plan->glyphset ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
     hb_vector_t<GlyphID> glyphs;
     hb_vector_t<HBUINT16> klasses;
@@ -1369,7 +1369,7 @@
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->glyphset;
+    const hb_set_t &glyphset = *c->plan->glyphset ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
     hb_vector_t<GlyphID> glyphs;
     hb_vector_t<HBUINT16> klasses;
diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh
index 33b8f0e..cc10634 100644
--- a/src/hb-ot-layout-gsub-table.hh
+++ b/src/hb-ot-layout-gsub-table.hh
@@ -105,7 +105,7 @@
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->glyphset;
+    const hb_set_t &glyphset = *c->plan->glyphset ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
     hb_vector_t<GlyphID> from;
     hb_vector_t<GlyphID> to;
@@ -202,7 +202,7 @@
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->glyphset;
+    const hb_set_t &glyphset = *c->plan->glyphset ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
     hb_vector_t<GlyphID> from;
     hb_vector_t<GlyphID> to;
diff --git a/src/hb-ot-maxp-table.hh b/src/hb-ot-maxp-table.hh
index e4b67ab..10bd592 100644
--- a/src/hb-ot-maxp-table.hh
+++ b/src/hb-ot-maxp-table.hh
@@ -105,7 +105,7 @@
     }
     maxp *maxp_prime = (maxp *) hb_blob_get_data (maxp_prime_blob, nullptr);
 
-    maxp_prime->set_num_glyphs (plan->glyphs.length);
+    maxp_prime->set_num_glyphs (plan->num_output_glyphs ());
     if (plan->drop_hints)
       drop_hint_fields (plan, maxp_prime);
 
diff --git a/src/hb-ot-vorg-table.hh b/src/hb-ot-vorg-table.hh
index 0202fcc..39073db 100644
--- a/src/hb-ot-vorg-table.hh
+++ b/src/hb-ot-vorg-table.hh
@@ -110,21 +110,29 @@
     /* count the number of glyphs to be included in the subset table */
     hb_vector_t<VertOriginMetric> subset_metrics;
     subset_metrics.init ();
-    unsigned int glyph = 0;
+
+
+    hb_codepoint_t old_glyph = HB_SET_VALUE_INVALID;
     unsigned int i = 0;
-    while ((glyph < plan->glyphs.length) && (i < vertYOrigins.len))
+    while (i < vertYOrigins.len
+           && plan->glyphset ()->next (&old_glyph))
     {
-      if (plan->glyphs[glyph] > vertYOrigins[i].glyph)
-        i++;
-      else if (plan->glyphs[glyph] < vertYOrigins[i].glyph)
-        glyph++;
-      else
+      while (old_glyph > vertYOrigins[i].glyph)
       {
-        VertOriginMetric *metrics = subset_metrics.push ();
-        metrics->glyph.set (glyph);
-        metrics->vertOriginY.set (vertYOrigins[i].vertOriginY);
-        glyph++;
         i++;
+        if (i >= vertYOrigins.len)
+          break;
+      }
+
+      if (old_glyph == vertYOrigins[i].glyph)
+      {
+        hb_codepoint_t new_glyph;
+        if (plan->new_gid_for_old_gid (old_glyph, &new_glyph))
+        {
+          VertOriginMetric *metrics = subset_metrics.push ();
+          metrics->glyph.set (new_glyph);
+          metrics->vertOriginY.set (vertYOrigins[i].vertOriginY);
+        }
       }
     }
 
diff --git a/src/hb-subset-cff1.cc b/src/hb-subset-cff1.cc
index 5133a4d..e015eda 100644
--- a/src/hb-subset-cff1.cc
+++ b/src/hb-subset-cff1.cc
@@ -469,11 +469,11 @@
     supp_size = 0;
     supp_codes.init ();
 
-    subset_enc_num_codes = plan->glyphs.length - 1;
+    subset_enc_num_codes = plan->glyphs_deprecated.length - 1;
     unsigned int glyph;
-    for (glyph = 1; glyph < plan->glyphs.length; glyph++)
+    for (glyph = 1; glyph < plan->glyphs_deprecated.length; glyph++)
     {
-      hb_codepoint_t  orig_glyph = plan->glyphs[glyph];
+      hb_codepoint_t  orig_glyph = plan->glyphs_deprecated[glyph];
       code = acc.glyph_to_code (orig_glyph);
       if (code == CFF_UNDEF_CODE)
       {
@@ -526,9 +526,9 @@
 
     subset_charset_ranges.resize (0);
     unsigned int glyph;
-    for (glyph = 1; glyph < plan->glyphs.length; glyph++)
+    for (glyph = 1; glyph < plan->glyphs_deprecated.length; glyph++)
     {
-      hb_codepoint_t  orig_glyph = plan->glyphs[glyph];
+      hb_codepoint_t  orig_glyph = plan->glyphs_deprecated[glyph];
       sid = acc.glyph_to_sid (orig_glyph);
 
       if (!acc.is_CID ())
@@ -544,7 +544,7 @@
 
     bool two_byte = subset_charset_ranges.finalize (glyph);
 
-    size0 = Charset0::min_size + HBUINT16::static_size * (plan->glyphs.length - 1);
+    size0 = Charset0::min_size + HBUINT16::static_size * (plan->glyphs_deprecated.length - 1);
     if (!two_byte)
       size_ranges = Charset1::min_size + Charset1_Range::static_size * subset_charset_ranges.length;
     else
@@ -559,7 +559,7 @@
 
     return Charset::calculate_serialized_size (
 			subset_charset_format,
-			subset_charset_format? subset_charset_ranges.length: plan->glyphs.length);
+			subset_charset_format? subset_charset_ranges.length: plan->glyphs_deprecated.length);
   }
 
   bool collect_sids_in_dicts (const OT::cff1::accelerator_subset_t &acc)
@@ -589,19 +589,19 @@
 		      hb_subset_plan_t *plan)
   {
      /* make sure notdef is first */
-    if ((plan->glyphs.length == 0) || (plan->glyphs[0] != 0)) return false;
+    if ((plan->glyphs_deprecated.length == 0) || (plan->glyphs_deprecated[0] != 0)) return false;
 
     final_size = 0;
-    num_glyphs = plan->glyphs.length;
+    num_glyphs = plan->glyphs_deprecated.length;
     orig_fdcount = acc.fdCount;
     drop_hints = plan->drop_hints;
     desubroutinize = plan->desubroutinize;
 
     /* check whether the subset renumbers any glyph IDs */
     gid_renum = false;
-    for (unsigned int glyph = 0; glyph < plan->glyphs.length; glyph++)
+    for (unsigned int glyph = 0; glyph < plan->glyphs_deprecated.length; glyph++)
     {
-      if (plan->glyphs[glyph] != glyph) {
+      if (plan->glyphs_deprecated[glyph] != glyph) {
 	gid_renum = true;
 	break;
       }
@@ -644,7 +644,7 @@
     /* Determine re-mapping of font index as fdmap among other info */
     if (acc.fdSelect != &Null(CFF1FDSelect))
     {
-	if (unlikely (!hb_plan_subset_cff_fdselect (plan->glyphs,
+	if (unlikely (!hb_plan_subset_cff_fdselect (plan->glyphs_deprecated,
 				  orig_fdcount,
 				  *acc.fdSelect,
 				  subset_fdcount,
@@ -681,7 +681,7 @@
     {
       /* Flatten global & local subrs */
       subr_flattener_t<const OT::cff1::accelerator_subset_t, cff1_cs_interp_env_t, cff1_cs_opset_flatten_t>
-		    flattener(acc, plan->glyphs, plan->drop_hints);
+		    flattener(acc, plan->glyphs_deprecated, plan->drop_hints);
       if (!flattener.flatten (subset_charstrings))
 	return false;
 
@@ -691,11 +691,11 @@
     else
     {
       /* Subset subrs: collect used subroutines, leaving all unused ones behind */
-      if (!subr_subsetter.subset (acc, plan->glyphs, plan->drop_hints))
+      if (!subr_subsetter.subset (acc, plan->glyphs_deprecated, plan->drop_hints))
 	return false;
 
       /* encode charstrings, global subrs, local subrs with new subroutine numbers */
-      if (!subr_subsetter.encode_charstrings (acc, plan->glyphs, subset_charstrings))
+      if (!subr_subsetter.encode_charstrings (acc, plan->glyphs_deprecated, subset_charstrings))
 	return false;
 
       if (!subr_subsetter.encode_globalsubrs (subset_globalsubrs))
@@ -784,7 +784,7 @@
       offsets.charStringsInfo.offSize = calcOffSize (dataSize);
       if (unlikely (offsets.charStringsInfo.offSize > 4))
       	return false;
-      final_size += CFF1CharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->glyphs.length, dataSize);
+      final_size += CFF1CharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->glyphs_deprecated.length, dataSize);
     }
 
     /* private dicts & local subrs */
@@ -816,7 +816,7 @@
     if (!acc.is_CID ())
       offsets.privateDictInfo = fontdicts_mod[0].privateDictInfo;
 
-    return ((subset_charstrings.length == plan->glyphs.length)
+    return ((subset_charstrings.length == plan->glyphs_deprecated.length)
 	   && (fontdicts_mod.length == subset_fdcount));
   }
 
@@ -1064,7 +1064,7 @@
   unsigned int  cff_prime_size = cff_plan.get_final_size ();
   char *cff_prime_data = (char *) calloc (1, cff_prime_size);
 
-  if (unlikely (!_write_cff1 (cff_plan, acc, plan->glyphs,
+  if (unlikely (!_write_cff1 (cff_plan, acc, plan->glyphs_deprecated,
 			      cff_prime_size, cff_prime_data))) {
     DEBUG_MSG(SUBSET, nullptr, "Failed to write a subset cff.");
     free (cff_prime_data);
diff --git a/src/hb-subset-cff2.cc b/src/hb-subset-cff2.cc
index 73a292d..4830b63 100644
--- a/src/hb-subset-cff2.cc
+++ b/src/hb-subset-cff2.cc
@@ -287,7 +287,7 @@
     {
       /* Flatten global & local subrs */
       subr_flattener_t<const OT::cff2::accelerator_subset_t, cff2_cs_interp_env_t, cff2_cs_opset_flatten_t>
-		    flattener(acc, plan->glyphs, plan->drop_hints);
+		    flattener(acc, plan->glyphs_deprecated, plan->drop_hints);
       if (!flattener.flatten (subset_charstrings))
 	return false;
 
@@ -297,11 +297,11 @@
     else
     {
       /* Subset subrs: collect used subroutines, leaving all unused ones behind */
-      if (!subr_subsetter.subset (acc, plan->glyphs, plan->drop_hints))
+      if (!subr_subsetter.subset (acc, plan->glyphs_deprecated, plan->drop_hints))
 	return false;
 
       /* encode charstrings, global subrs, local subrs with new subroutine numbers */
-      if (!subr_subsetter.encode_charstrings (acc, plan->glyphs, subset_charstrings))
+      if (!subr_subsetter.encode_charstrings (acc, plan->glyphs_deprecated, subset_charstrings))
 	return false;
 
       if (!subr_subsetter.encode_globalsubrs (subset_globalsubrs))
@@ -352,7 +352,7 @@
     if (acc.fdSelect != &Null(CFF2FDSelect))
     {
       offsets.FDSelectInfo.offset = final_size;
-      if (unlikely (!hb_plan_subset_cff_fdselect (plan->glyphs,
+      if (unlikely (!hb_plan_subset_cff_fdselect (plan->glyphs_deprecated,
 				  orig_fdcount,
 				  *(const FDSelect *)acc.fdSelect,
 				  subset_fdcount,
@@ -385,7 +385,7 @@
       offsets.charStringsInfo.offset = final_size;
       unsigned int dataSize = subset_charstrings.total_size ();
       offsets.charStringsInfo.offSize = calcOffSize (dataSize);
-      final_size += CFF2CharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->glyphs.length, dataSize);
+      final_size += CFF2CharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->glyphs_deprecated.length, dataSize);
     }
 
     /* private dicts & local subrs */
@@ -584,7 +584,7 @@
   unsigned int  cff2_prime_size = cff2_plan.get_final_size ();
   char *cff2_prime_data = (char *) calloc (1, cff2_prime_size);
 
-  if (unlikely (!_write_cff2 (cff2_plan, acc, plan->glyphs,
+  if (unlikely (!_write_cff2 (cff2_plan, acc, plan->glyphs_deprecated,
 			      cff2_prime_size, cff2_prime_data))) {
     DEBUG_MSG(SUBSET, nullptr, "Failed to write a subset cff2.");
     free (cff2_prime_data);
diff --git a/src/hb-subset-glyf.cc b/src/hb-subset-glyf.cc
index cca364d..ee004ee 100644
--- a/src/hb-subset-glyf.cc
+++ b/src/hb-subset-glyf.cc
@@ -29,97 +29,128 @@
 #include "hb-set.h"
 #include "hb-subset-glyf.hh"
 
-static bool
-_calculate_glyf_and_loca_prime_size (const OT::glyf::accelerator_t &glyf,
-				     hb_vector_t<hb_codepoint_t> &glyph_ids,
-				     hb_bool_t drop_hints,
-				     bool *use_short_loca /* OUT */,
-				     unsigned int *glyf_size /* OUT */,
-				     unsigned int *loca_size /* OUT */,
-				     hb_vector_t<unsigned int> *instruction_ranges /* OUT */)
+struct loca_data_t
 {
-  unsigned int total = 0;
-  for (unsigned int i = 0; i < glyph_ids.length; i++)
+  bool          is_short;
+  void         *data;
+  unsigned int  size;
+
+  inline bool
+  _write_loca_entry (unsigned int  id,
+                     unsigned int  offset)
   {
-    hb_codepoint_t next_glyph = glyph_ids[i];
-    if (!instruction_ranges->resize (instruction_ranges->length + 2))
+    unsigned int entry_size = is_short ? sizeof (OT::HBUINT16) : sizeof (OT::HBUINT32);
+    if ((id + 1) * entry_size <= size)
     {
-      DEBUG_MSG(SUBSET, nullptr, "Failed to resize instruction_ranges.");
+      if (is_short) {
+        ((OT::HBUINT16*) data) [id].set (offset / 2);
+      } else {
+        ((OT::HBUINT32*) data) [id].set (offset);
+      }
+      return true;
+    }
+
+    // Offset was not written because the write is out of bounds.
+    DEBUG_MSG(SUBSET,
+              nullptr,
+              "WARNING: Attempted to write an out of bounds loca entry at index %d. Loca size is %d.",
+              id,
+              size);
+    return false;
+  }
+};
+
+/**
+ * If hints are being dropped find the range which in glyf at which
+ * the hinting instructions are located. Add them to the instruction_ranges
+ * vector.
+ */
+static bool
+_add_instructions_range (const OT::glyf::accelerator_t &glyf,
+                         hb_codepoint_t                 glyph_id,
+                         unsigned int                   glyph_start_offset,
+                         unsigned int                   glyph_end_offset,
+                         bool                           drop_hints,
+                         hb_vector_t<unsigned int>     *instruction_ranges /* OUT */)
+{
+  if (!instruction_ranges->resize (instruction_ranges->length + 2))
+  {
+    DEBUG_MSG(SUBSET, nullptr, "Failed to resize instruction_ranges.");
+    return false;
+  }
+  unsigned int *instruction_start = &(*instruction_ranges)[instruction_ranges->length - 2];
+  *instruction_start = 0;
+  unsigned int *instruction_end = &(*instruction_ranges)[instruction_ranges->length - 1];
+  *instruction_end = 0;
+
+  if (drop_hints)
+  {
+    if (unlikely (!glyf.get_instruction_offsets (glyph_start_offset, glyph_end_offset,
+                                                 instruction_start, instruction_end)))
+    {
+      DEBUG_MSG(SUBSET, nullptr, "Unable to get instruction offsets for %d", glyph_id);
       return false;
     }
-    unsigned int *instruction_start = &(*instruction_ranges)[instruction_ranges->length - 2];
-    *instruction_start = 0;
-    unsigned int *instruction_end = &(*instruction_ranges)[instruction_ranges->length - 1];
-    *instruction_end = 0;
+  }
 
+  return true;
+}
+
+static bool
+_calculate_glyf_and_loca_prime_size (const OT::glyf::accelerator_t &glyf,
+                                     const hb_subset_plan_t        *plan,
+                                     loca_data_t                   *loca_data, /* OUT */
+				     unsigned int                  *glyf_size /* OUT */,
+				     hb_vector_t<unsigned int>     *instruction_ranges /* OUT */)
+{
+  unsigned int total = 0;
+
+  hb_codepoint_t next_glyph = HB_SET_VALUE_INVALID;
+  while (plan->glyphset ()->next (&next_glyph))
+  {
     unsigned int start_offset, end_offset;
     if (unlikely (!(glyf.get_offsets (next_glyph, &start_offset, &end_offset) &&
 		    glyf.remove_padding (start_offset, &end_offset))))
     {
       DEBUG_MSG(SUBSET, nullptr, "Invalid gid %d", next_glyph);
-      continue;
+      start_offset = end_offset = 0;
     }
-    if (end_offset - start_offset < OT::glyf::GlyphHeader::static_size)
+
+    bool is_zero_length = end_offset - start_offset < OT::glyf::GlyphHeader::static_size;
+    if (!_add_instructions_range (glyf,
+                                  next_glyph,
+                                  start_offset,
+                                  end_offset,
+                                  plan->drop_hints && !is_zero_length,
+                                  instruction_ranges))
+      return false;
+
+    if (is_zero_length)
       continue; /* 0-length glyph */
 
-    if (drop_hints)
-    {
-      if (unlikely (!glyf.get_instruction_offsets (start_offset, end_offset,
-						   instruction_start, instruction_end)))
-      {
-	DEBUG_MSG(SUBSET, nullptr, "Unable to get instruction offsets for %d", next_glyph);
-	return false;
-      }
-    }
-
-    total += end_offset - start_offset - (*instruction_end - *instruction_start);
+    total += end_offset - start_offset
+             - ((*instruction_ranges)[instruction_ranges->length - 1]
+                - (*instruction_ranges)[instruction_ranges->length - 2]);
     /* round2 so short loca will work */
     total += total % 2;
   }
 
   *glyf_size = total;
-  *use_short_loca = (total <= 131070);
-  *loca_size = (glyph_ids.length + 1)
-      * (*use_short_loca ? sizeof (OT::HBUINT16) : sizeof (OT::HBUINT32));
+  loca_data->is_short = (total <= 131070);
+  loca_data->size = (plan->num_output_glyphs () + 1)
+      * (loca_data->is_short ? sizeof (OT::HBUINT16) : sizeof (OT::HBUINT32));
 
   DEBUG_MSG(SUBSET, nullptr, "preparing to subset glyf: final size %d, loca size %d, using %s loca",
 	    total,
-	    *loca_size,
-	    *use_short_loca ? "short" : "long");
+	    loca_data->size,
+	    loca_data->is_short ? "short" : "long");
   return true;
 }
 
-static bool
-_write_loca_entry (unsigned int  id,
-		   unsigned int  offset,
-		   bool          is_short,
-		   void         *loca_prime,
-		   unsigned int  loca_size)
-{
-  unsigned int entry_size = is_short ? sizeof (OT::HBUINT16) : sizeof (OT::HBUINT32);
-  if ((id + 1) * entry_size <= loca_size)
-  {
-    if (is_short) {
-      ((OT::HBUINT16*) loca_prime) [id].set (offset / 2);
-    } else {
-      ((OT::HBUINT32*) loca_prime) [id].set (offset);
-    }
-    return true;
-  }
-
-  // Offset was not written because the write is out of bounds.
-  DEBUG_MSG(SUBSET,
-	    nullptr,
-	    "WARNING: Attempted to write an out of bounds loca entry at index %d. Loca size is %d.",
-	    id,
-	    loca_size);
-  return false;
-}
-
 static void
-_update_components (hb_subset_plan_t * plan,
-		    char * glyph_start,
-		    unsigned int length)
+_update_components (const hb_subset_plan_t *plan,
+		    char                   *glyph_start,
+		    unsigned int            length)
 {
   OT::glyf::CompositeGlyphHeader::Iterator iterator;
   if (OT::glyf::CompositeGlyphHeader::get_iterator (glyph_start,
@@ -153,24 +184,35 @@
 }
 
 static bool
-_write_glyf_and_loca_prime (hb_subset_plan_t              *plan,
+_write_glyf_and_loca_prime (const hb_subset_plan_t        *plan,
 			    const OT::glyf::accelerator_t &glyf,
 			    const char                    *glyf_data,
-			    bool                           use_short_loca,
-			    hb_vector_t<unsigned int> &instruction_ranges,
+			    hb_vector_t<unsigned int>     &instruction_ranges,
 			    unsigned int                   glyf_prime_size,
 			    char                          *glyf_prime_data /* OUT */,
-			    unsigned int                   loca_prime_size,
-			    char                          *loca_prime_data /* OUT */)
+			    loca_data_t                   *loca_prime /* OUT */)
 {
-  hb_vector_t<hb_codepoint_t> &glyph_ids = plan->glyphs;
   char *glyf_prime_data_next = glyf_prime_data;
 
   bool success = true;
-  for (unsigned int i = 0; i < glyph_ids.length; i++)
+
+
+  unsigned int i = 0;
+  hb_codepoint_t new_gid;
+  for (new_gid = 0; new_gid < plan->num_output_glyphs (); new_gid++)
   {
+    hb_codepoint_t old_gid;
+    if (!plan->old_gid_for_new_gid (new_gid, &old_gid))
+    {
+      // Empty glyph, add a loca entry and carry on.
+      loca_prime->_write_loca_entry (new_gid,
+                                     glyf_prime_data_next - glyf_prime_data);
+      continue;
+    }
+
+
     unsigned int start_offset, end_offset;
-    if (unlikely (!(glyf.get_offsets (glyph_ids[i], &start_offset, &end_offset) &&
+    if (unlikely (!(glyf.get_offsets (old_gid, &start_offset, &end_offset) &&
 		    glyf.remove_padding (start_offset, &end_offset))))
       end_offset = start_offset = 0;
 
@@ -182,9 +224,9 @@
     if (glyf_prime_data_next + length > glyf_prime_data + glyf_prime_size)
     {
       DEBUG_MSG(SUBSET,
-		 nullptr,
-		 "WARNING: Attempted to write an out of bounds glyph entry for gid %d (length %d)",
-		 i, length);
+                nullptr,
+                "WARNING: Attempted to write an out of bounds glyph entry for gid %d (length %d)",
+                i, length);
       return false;
     }
 
@@ -204,22 +246,20 @@
 	memset (glyf_prime_data_next + instruction_start - start_offset - 2, 0, 2);
     }
 
-    success = success && _write_loca_entry (i,
-					    glyf_prime_data_next - glyf_prime_data,
-					    use_short_loca,
-					    loca_prime_data,
-					    loca_prime_size);
+    success = success && loca_prime->_write_loca_entry (new_gid,
+                                                        glyf_prime_data_next - glyf_prime_data);
     _update_components (plan, glyf_prime_data_next, length);
 
     // TODO: don't align to two bytes if using long loca.
     glyf_prime_data_next += length + (length % 2); // Align to 2 bytes for short loca.
+
+    i++;
   }
 
-  success = success && _write_loca_entry (glyph_ids.length,
-					  glyf_prime_data_next - glyf_prime_data,
-					  use_short_loca,
-					  loca_prime_data,
-					  loca_prime_size);
+  // loca table has n+1 entries where the last entry signifies the end location of the last
+  // glyph.
+  success = success && loca_prime->_write_loca_entry (new_gid,
+                                                      glyf_prime_data_next - glyf_prime_data);
   return success;
 }
 
@@ -228,52 +268,48 @@
 			  const char                     *glyf_data,
 			  hb_subset_plan_t               *plan,
 			  bool                           *use_short_loca,
-			  hb_blob_t                     **glyf_prime /* OUT */,
-			  hb_blob_t                     **loca_prime /* OUT */)
+			  hb_blob_t                     **glyf_prime_blob /* OUT */,
+			  hb_blob_t                     **loca_prime_blob /* OUT */)
 {
   // TODO(grieger): Sanity check allocation size for the new table.
-  hb_vector_t<hb_codepoint_t> &glyphs_to_retain = plan->glyphs;
-
+  loca_data_t loca_prime;
   unsigned int glyf_prime_size;
-  unsigned int loca_prime_size;
   hb_vector_t<unsigned int> instruction_ranges;
   instruction_ranges.init ();
 
   if (unlikely (!_calculate_glyf_and_loca_prime_size (glyf,
-						      glyphs_to_retain,
-						      plan->drop_hints,
-						      use_short_loca,
+                                                      plan,
+                                                      &loca_prime,
 						      &glyf_prime_size,
-						      &loca_prime_size,
 						      &instruction_ranges))) {
     instruction_ranges.fini ();
     return false;
   }
+  *use_short_loca = loca_prime.is_short;
 
   char *glyf_prime_data = (char *) calloc (1, glyf_prime_size);
-  char *loca_prime_data = (char *) calloc (1, loca_prime_size);
+  loca_prime.data = (void *) calloc (1, loca_prime.size);
   if (unlikely (!_write_glyf_and_loca_prime (plan, glyf, glyf_data,
-					     *use_short_loca,
 					     instruction_ranges,
 					     glyf_prime_size, glyf_prime_data,
-					     loca_prime_size, loca_prime_data))) {
+					     &loca_prime))) {
     free (glyf_prime_data);
-    free (loca_prime_data);
+    free (loca_prime.data);
     instruction_ranges.fini ();
     return false;
   }
   instruction_ranges.fini ();
 
-  *glyf_prime = hb_blob_create (glyf_prime_data,
-				glyf_prime_size,
-				HB_MEMORY_MODE_READONLY,
-				glyf_prime_data,
-				free);
-  *loca_prime = hb_blob_create (loca_prime_data,
-				loca_prime_size,
-				HB_MEMORY_MODE_READONLY,
-				loca_prime_data,
-				free);
+  *glyf_prime_blob = hb_blob_create (glyf_prime_data,
+                                     glyf_prime_size,
+                                     HB_MEMORY_MODE_READONLY,
+                                     glyf_prime_data,
+                                     free);
+  *loca_prime_blob = hb_blob_create ((char *) loca_prime.data,
+                                     loca_prime.size,
+                                     HB_MEMORY_MODE_READONLY,
+                                     loca_prime.data,
+                                     free);
   return true;
 }
 
diff --git a/src/hb-subset-input.cc b/src/hb-subset-input.cc
index f718a56..693c9c2 100644
--- a/src/hb-subset-input.cc
+++ b/src/hb-subset-input.cc
@@ -44,7 +44,10 @@
 
   input->unicodes = hb_set_create ();
   input->glyphs = hb_set_create ();
+  input->drop_hints = false;
   input->drop_layout = true;
+  input->desubroutinize = false;
+  input->retain_gids = false;
 
   return input;
 }
@@ -144,3 +147,27 @@
 {
   return subset_input->desubroutinize;
 }
+
+/**
+ * hb_subset_input_set_retain_gids:
+ * @subset_input: a subset_input.
+ * @retain_gids: If true the subsetter will not renumber glyph ids.
+ * Since: REPLACEME
+ **/
+HB_EXTERN void
+hb_subset_input_set_retain_gids (hb_subset_input_t *subset_input,
+				 hb_bool_t retain_gids)
+{
+  subset_input->retain_gids = retain_gids;
+}
+
+/**
+ * hb_subset_input_get_retain_gids:
+ * Returns: value of retain_gids.
+ * Since: REPLACEME
+ **/
+HB_EXTERN hb_bool_t
+hb_subset_input_get_retain_gids (hb_subset_input_t *subset_input)
+{
+  return subset_input->retain_gids;
+}
diff --git a/src/hb-subset-input.hh b/src/hb-subset-input.hh
index 8dad94f..04d6e12 100644
--- a/src/hb-subset-input.hh
+++ b/src/hb-subset-input.hh
@@ -44,6 +44,7 @@
   bool drop_hints : 1;
   bool drop_layout : 1;
   bool desubroutinize : 1;
+  bool retain_gids : 1;
   /* TODO
    *
    * features
diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc
index cff3426..5702d01 100644
--- a/src/hb-subset-plan.cc
+++ b/src/hb-subset-plan.cc
@@ -156,11 +156,32 @@
 }
 
 static void
-_create_old_gid_to_new_gid_map (const hb_vector_t<hb_codepoint_t> &glyphs,
-				hb_map_t *glyph_map)
+_create_old_gid_to_new_gid_map (const hb_face_t                   *face,
+                                bool                               retain_gids,
+				const hb_vector_t<hb_codepoint_t> &glyphs,
+                                hb_map_t                          *glyph_map, /* OUT */
+                                hb_map_t                          *reverse_glyph_map, /* OUT */
+                                unsigned int                      *num_glyphs /* OUT */)
 {
   for (unsigned int i = 0; i < glyphs.length; i++) {
-    glyph_map->set (glyphs[i], i);
+    if (!retain_gids)
+    {
+      glyph_map->set (glyphs[i], i);
+      reverse_glyph_map->set (i, glyphs[i]);
+    }
+    else
+    {
+      glyph_map->set (glyphs[i], glyphs[i]);
+      reverse_glyph_map->set (glyphs[i], glyphs[i]);
+    }
+  }
+  if (!retain_gids || glyphs.length == 0)
+  {
+    *num_glyphs = glyphs.length;
+  }
+  else
+  {
+    *num_glyphs = face->get_num_glyphs ();
   }
 }
 
@@ -184,19 +205,25 @@
   plan->drop_layout = input->drop_layout;
   plan->desubroutinize = input->desubroutinize;
   plan->unicodes = hb_set_create();
-  plan->glyphs.init();
+  plan->glyphs_deprecated.init();
   plan->source = hb_face_reference (face);
   plan->dest = hb_face_builder_create ();
   plan->codepoint_to_glyph = hb_map_create();
   plan->glyph_map = hb_map_create();
-  plan->glyphset = _populate_gids_to_retain (face,
-					     input->unicodes,
-					     !plan->drop_layout,
-					     plan->unicodes,
-					     plan->codepoint_to_glyph,
-					     &plan->glyphs);
-  _create_old_gid_to_new_gid_map (plan->glyphs,
-				  plan->glyph_map);
+  plan->reverse_glyph_map = hb_map_create();
+  plan->_glyphset = _populate_gids_to_retain (face,
+                                              input->unicodes,
+                                              !plan->drop_layout,
+                                              plan->unicodes,
+                                              plan->codepoint_to_glyph,
+                                              &plan->glyphs_deprecated);
+
+  _create_old_gid_to_new_gid_map (face,
+                                  input->retain_gids,
+				  plan->glyphs_deprecated,
+				  plan->glyph_map,
+                                  plan->reverse_glyph_map,
+                                  &plan->_num_output_glyphs);
 
   return plan;
 }
@@ -212,12 +239,13 @@
   if (!hb_object_destroy (plan)) return;
 
   hb_set_destroy (plan->unicodes);
-  plan->glyphs.fini ();
+  plan->glyphs_deprecated.fini ();
   hb_face_destroy (plan->source);
   hb_face_destroy (plan->dest);
   hb_map_destroy (plan->codepoint_to_glyph);
   hb_map_destroy (plan->glyph_map);
-  hb_set_destroy (plan->glyphset);
+  hb_map_destroy (plan->reverse_glyph_map);
+  hb_set_destroy (plan->_glyphset);
 
   free (plan);
 }
diff --git a/src/hb-subset-plan.hh b/src/hb-subset-plan.hh
index a710a4d..32c1999 100644
--- a/src/hb-subset-plan.hh
+++ b/src/hb-subset-plan.hh
@@ -33,6 +33,7 @@
 #include "hb-subset-input.hh"
 
 #include "hb-map.hh"
+#include "hb-set.hh"
 
 struct hb_subset_plan_t
 {
@@ -45,18 +46,54 @@
   // For each cp that we'd like to retain maps to the corresponding gid.
   hb_set_t *unicodes;
 
-  hb_vector_t<hb_codepoint_t> glyphs;
-  hb_set_t *glyphset;
-
+  // The glyph subset
   hb_map_t *codepoint_to_glyph;
+
+  // Old -> New glyph id mapping
   hb_map_t *glyph_map;
+  hb_map_t *reverse_glyph_map;
+
+  // Deprecated members:
+  hb_vector_t<hb_codepoint_t> glyphs_deprecated;
 
   // Plan is only good for a specific source/dest so keep them with it
   hb_face_t *source;
   hb_face_t *dest;
 
-  bool new_gid_for_codepoint (hb_codepoint_t codepoint,
-			      hb_codepoint_t *new_gid) const
+  unsigned int _num_output_glyphs;
+  hb_set_t *_glyphset;
+
+ public:
+
+  /*
+   * The set of input glyph ids which will be retained in the subset.
+   */
+  inline const hb_set_t *
+  glyphset () const
+  {
+    return _glyphset;
+  }
+
+  /*
+   * The total number of output glyphs in the final subset.
+   */
+  inline unsigned int
+  num_output_glyphs () const
+  {
+    return _num_output_glyphs;
+  }
+
+  /*
+   * Given an output gid , returns true if that glyph id is an empty
+   * glyph (ie. it's a gid that we are dropping all data for).
+   */
+  inline bool is_empty_glyph (hb_codepoint_t gid) const
+  {
+    return !_glyphset->has (gid);
+  }
+
+  inline bool new_gid_for_codepoint (hb_codepoint_t codepoint,
+                                     hb_codepoint_t *new_gid) const
   {
     hb_codepoint_t old_gid = codepoint_to_glyph->get (codepoint);
     if (old_gid == HB_MAP_VALUE_INVALID)
@@ -65,8 +102,8 @@
     return new_gid_for_old_gid (old_gid, new_gid);
   }
 
-  bool new_gid_for_old_gid (hb_codepoint_t old_gid,
-			    hb_codepoint_t *new_gid) const
+  inline bool new_gid_for_old_gid (hb_codepoint_t old_gid,
+                                   hb_codepoint_t *new_gid) const
   {
     hb_codepoint_t gid = glyph_map->get (old_gid);
     if (gid == HB_MAP_VALUE_INVALID)
@@ -76,7 +113,18 @@
     return true;
   }
 
-  bool
+  inline bool old_gid_for_new_gid (hb_codepoint_t  new_gid,
+                                   hb_codepoint_t *old_gid) const
+  {
+    hb_codepoint_t gid = reverse_glyph_map->get (new_gid);
+    if (gid == HB_MAP_VALUE_INVALID)
+      return false;
+
+    *old_gid = gid;
+    return true;
+  }
+
+  inline bool
   add_table (hb_tag_t tag,
 	     hb_blob_t *contents)
   {
diff --git a/src/hb-subset.cc b/src/hb-subset.cc
index 37e7cec..135265f 100644
--- a/src/hb-subset.cc
+++ b/src/hb-subset.cc
@@ -52,7 +52,7 @@
 				  unsigned int table_len)
 {
   unsigned int src_glyphs = plan->source->get_num_glyphs ();
-  unsigned int dst_glyphs = plan->glyphset->get_population ();
+  unsigned int dst_glyphs = plan->glyphset ()->get_population ();
 
   if (unlikely (!src_glyphs))
     return 512 + table_len;
diff --git a/src/hb-subset.h b/src/hb-subset.h
index f582e46..657709e 100644
--- a/src/hb-subset.h
+++ b/src/hb-subset.h
@@ -72,6 +72,12 @@
 HB_EXTERN hb_bool_t
 hb_subset_input_get_desubroutinize (hb_subset_input_t *subset_input);
 
+HB_EXTERN void
+hb_subset_input_set_retain_gids (hb_subset_input_t *subset_input,
+				 hb_bool_t retain_gids);
+HB_EXTERN hb_bool_t
+hb_subset_input_get_retain_gids (hb_subset_input_t *subset_input);
+
 /* hb_subset () */
 HB_EXTERN hb_face_t *
 hb_subset (hb_face_t *source, hb_subset_input_t *input);
diff --git a/src/hb-version.h b/src/hb-version.h
index 0c82d5b..13db8ce 100644
--- a/src/hb-version.h
+++ b/src/hb-version.h
@@ -38,9 +38,9 @@
 
 #define HB_VERSION_MAJOR 2
 #define HB_VERSION_MINOR 3
-#define HB_VERSION_MICRO 0
+#define HB_VERSION_MICRO 1
 
-#define HB_VERSION_STRING "2.3.0"
+#define HB_VERSION_STRING "2.3.1"
 
 #define HB_VERSION_ATLEAST(major,minor,micro) \
 	((major)*10000+(minor)*100+(micro) <= \
diff --git a/test/api/fonts/Roboto-Regular.ac.retaingids.ttf b/test/api/fonts/Roboto-Regular.ac.retaingids.ttf
new file mode 100644
index 0000000..8606a55
--- /dev/null
+++ b/test/api/fonts/Roboto-Regular.ac.retaingids.ttf
Binary files differ
diff --git a/test/api/test-subset-glyf.c b/test/api/test-subset-glyf.c
index 0e5c293..e8609ca 100644
--- a/test/api/test-subset-glyf.c
+++ b/test/api/test-subset-glyf.c
@@ -257,6 +257,31 @@
   hb_face_destroy (face);
 }
 
+static void
+test_subset_glyf_retain_gids (void)
+{
+  hb_face_t *face_abc = hb_test_open_font_file ("fonts/Roboto-Regular.abc.ttf");
+  hb_face_t *face_ac = hb_test_open_font_file ("fonts/Roboto-Regular.ac.retaingids.ttf");
+
+  hb_set_t *codepoints = hb_set_create();
+  hb_face_t *face_abc_subset;
+  hb_set_add (codepoints, 97);
+  hb_set_add (codepoints, 99);
+
+  hb_subset_input_t *input = hb_subset_test_create_input (codepoints);
+  hb_subset_input_set_retain_gids (input, true);
+  face_abc_subset = hb_subset_test_create_subset (face_abc, input);
+  hb_set_destroy (codepoints);
+
+  hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('g','l','y','f'));
+  hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('l','o','c', 'a'));
+  check_maxp_num_glyphs(face_abc_subset, 4, true);
+
+  hb_face_destroy (face_abc_subset);
+  hb_face_destroy (face_abc);
+  hb_face_destroy (face_ac);
+}
+
 // TODO(grieger): test for long loca generation.
 
 int
@@ -272,6 +297,7 @@
   hb_test_add (test_subset_glyf_with_components);
   hb_test_add (test_subset_glyf_with_gsub);
   hb_test_add (test_subset_glyf_without_gsub);
+  hb_test_add (test_subset_glyf_retain_gids);
 
   return hb_test_run();
 }
diff --git a/test/fuzzing/hb-subset-fuzzer.cc b/test/fuzzing/hb-subset-fuzzer.cc
index 3a71f22..56ffd22 100644
--- a/test/fuzzing/hb-subset-fuzzer.cc
+++ b/test/fuzzing/hb-subset-fuzzer.cc
@@ -11,11 +11,13 @@
 	   const hb_codepoint_t text[],
 	   int text_length,
 	   bool drop_hints,
-	   bool drop_layout)
+	   bool drop_layout,
+	   bool retain_gids)
 {
   hb_subset_input_t *input = hb_subset_input_create_or_fail ();
   hb_subset_input_set_drop_hints (input, drop_hints);
   hb_subset_input_set_drop_layout (input, drop_layout);
+  hb_subset_input_set_retain_gids (input, retain_gids);
   hb_set_t *codepoints = hb_subset_input_unicode_set (input);
 
   for (int i = 0; i < text_length; i++)
@@ -32,16 +34,14 @@
 static void
 trySubset (hb_face_t *face,
 	   const hb_codepoint_t text[],
-	   int text_length)
+	   int text_length,
+	   const uint8_t flags[1])
 {
-  for (unsigned int drop_hints = 0; drop_hints < 2; drop_hints++)
-  {
-    for (unsigned int drop_layout = 0; drop_layout < 2; drop_layout++)
-    {
-      trySubset (face, text, text_length,
-		 (bool) drop_hints, (bool) drop_layout);
-    }
-  }
+  bool drop_hints =  flags[0] & (1 << 0);
+  bool drop_layout = flags[0] & (1 << 1);
+  bool retain_gids = flags[0] & (1 << 2);
+  trySubset (face, text, text_length,
+	     drop_hints, drop_layout, retain_gids);
 }
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
@@ -55,21 +55,27 @@
   hb_face_collect_unicodes (face, output);
   hb_set_destroy (output);
 
+  uint8_t flags[1] = {0};
   const hb_codepoint_t text[] =
       {
 	'A', 'B', 'C', 'D', 'E', 'X', 'Y', 'Z', '1', '2',
 	'3', '@', '_', '%', '&', ')', '*', '$', '!'
       };
 
-  trySubset (face, text, sizeof (text) / sizeof (hb_codepoint_t));
+  trySubset (face, text, sizeof (text) / sizeof (hb_codepoint_t), flags);
 
   hb_codepoint_t text_from_data[16];
-  if (size > sizeof(text_from_data)) {
+  if (size > sizeof(text_from_data) + sizeof(flags)) {
     memcpy (text_from_data,
 	    data + size - sizeof(text_from_data),
 	    sizeof(text_from_data));
+
+    memcpy (flags,
+	    data + size - sizeof(text_from_data) - sizeof(flags),
+	    sizeof(flags));
     unsigned int text_size = sizeof (text_from_data) / sizeof (hb_codepoint_t);
-    trySubset (face, text_from_data, text_size);
+
+    trySubset (face, text_from_data, text_size, flags);
   }
 
   hb_face_destroy (face);
diff --git a/test/subset/data/expected/basics/Roboto-Regular.abc.drop-hints-retain-gids.61,62,63.ttf b/test/subset/data/expected/basics/Roboto-Regular.abc.drop-hints-retain-gids.61,62,63.ttf
new file mode 100644
index 0000000..52dc474
--- /dev/null
+++ b/test/subset/data/expected/basics/Roboto-Regular.abc.drop-hints-retain-gids.61,62,63.ttf
Binary files differ
diff --git a/test/subset/data/expected/basics/Roboto-Regular.abc.drop-hints-retain-gids.61,63.ttf b/test/subset/data/expected/basics/Roboto-Regular.abc.drop-hints-retain-gids.61,63.ttf
new file mode 100644
index 0000000..d6c516e
--- /dev/null
+++ b/test/subset/data/expected/basics/Roboto-Regular.abc.drop-hints-retain-gids.61,63.ttf
Binary files differ
diff --git a/test/subset/data/expected/basics/Roboto-Regular.abc.drop-hints-retain-gids.61.ttf b/test/subset/data/expected/basics/Roboto-Regular.abc.drop-hints-retain-gids.61.ttf
new file mode 100644
index 0000000..1a0d5bd
--- /dev/null
+++ b/test/subset/data/expected/basics/Roboto-Regular.abc.drop-hints-retain-gids.61.ttf
Binary files differ
diff --git a/test/subset/data/expected/basics/Roboto-Regular.abc.drop-hints-retain-gids.62.ttf b/test/subset/data/expected/basics/Roboto-Regular.abc.drop-hints-retain-gids.62.ttf
new file mode 100644
index 0000000..257184b
--- /dev/null
+++ b/test/subset/data/expected/basics/Roboto-Regular.abc.drop-hints-retain-gids.62.ttf
Binary files differ
diff --git a/test/subset/data/expected/basics/Roboto-Regular.abc.drop-hints-retain-gids.63.ttf b/test/subset/data/expected/basics/Roboto-Regular.abc.drop-hints-retain-gids.63.ttf
new file mode 100644
index 0000000..ac735b3
--- /dev/null
+++ b/test/subset/data/expected/basics/Roboto-Regular.abc.drop-hints-retain-gids.63.ttf
Binary files differ
diff --git a/test/subset/data/expected/basics/Roboto-Regular.abc.retain-gids.61,62,63.ttf b/test/subset/data/expected/basics/Roboto-Regular.abc.retain-gids.61,62,63.ttf
new file mode 100644
index 0000000..12d9208
--- /dev/null
+++ b/test/subset/data/expected/basics/Roboto-Regular.abc.retain-gids.61,62,63.ttf
Binary files differ
diff --git a/test/subset/data/expected/basics/Roboto-Regular.abc.retain-gids.61,63.ttf b/test/subset/data/expected/basics/Roboto-Regular.abc.retain-gids.61,63.ttf
new file mode 100644
index 0000000..f545375
--- /dev/null
+++ b/test/subset/data/expected/basics/Roboto-Regular.abc.retain-gids.61,63.ttf
Binary files differ
diff --git a/test/subset/data/expected/basics/Roboto-Regular.abc.retain-gids.61.ttf b/test/subset/data/expected/basics/Roboto-Regular.abc.retain-gids.61.ttf
new file mode 100644
index 0000000..d3a67ea
--- /dev/null
+++ b/test/subset/data/expected/basics/Roboto-Regular.abc.retain-gids.61.ttf
Binary files differ
diff --git a/test/subset/data/expected/basics/Roboto-Regular.abc.retain-gids.62.ttf b/test/subset/data/expected/basics/Roboto-Regular.abc.retain-gids.62.ttf
new file mode 100644
index 0000000..4ff6e33
--- /dev/null
+++ b/test/subset/data/expected/basics/Roboto-Regular.abc.retain-gids.62.ttf
Binary files differ
diff --git a/test/subset/data/expected/basics/Roboto-Regular.abc.retain-gids.63.ttf b/test/subset/data/expected/basics/Roboto-Regular.abc.retain-gids.63.ttf
new file mode 100644
index 0000000..efd7c16
--- /dev/null
+++ b/test/subset/data/expected/basics/Roboto-Regular.abc.retain-gids.63.ttf
Binary files differ
diff --git a/test/subset/data/profiles/drop-hints-retain-gids.txt b/test/subset/data/profiles/drop-hints-retain-gids.txt
new file mode 100644
index 0000000..b0409b8
--- /dev/null
+++ b/test/subset/data/profiles/drop-hints-retain-gids.txt
@@ -0,0 +1,2 @@
+--no-hinting
+--retain-gids
diff --git a/test/subset/data/profiles/retain-gids.txt b/test/subset/data/profiles/retain-gids.txt
new file mode 100644
index 0000000..d757487
--- /dev/null
+++ b/test/subset/data/profiles/retain-gids.txt
@@ -0,0 +1 @@
+--retain-gids
diff --git a/test/subset/data/tests/basics.tests b/test/subset/data/tests/basics.tests
index 9725445..4fc3f4e 100644
--- a/test/subset/data/tests/basics.tests
+++ b/test/subset/data/tests/basics.tests
@@ -4,6 +4,8 @@
 PROFILES:
 default.txt
 drop-hints.txt
+drop-hints-retain-gids.txt
+retain-gids.txt
 
 SUBSETS:
 abc
diff --git a/util/hb-subset.cc b/util/hb-subset.cc
index b7d9eb9..33e584b 100644
--- a/util/hb-subset.cc
+++ b/util/hb-subset.cc
@@ -91,6 +91,7 @@
   {
     hb_subset_input_set_drop_layout (input, !subset_options.keep_layout);
     hb_subset_input_set_drop_hints (input, subset_options.drop_hints);
+    hb_subset_input_set_retain_gids (input, subset_options.retain_gids);
     hb_subset_input_set_desubroutinize (input, subset_options.desubroutinize);
 
     hb_face_t *face = hb_font_get_face (font);
diff --git a/util/options.cc b/util/options.cc
index 04ddcf6..b315c6a 100644
--- a/util/options.cc
+++ b/util/options.cc
@@ -977,6 +977,7 @@
   {
     {"layout", 0, 0, G_OPTION_ARG_NONE,  &this->keep_layout,   "Keep OpenType Layout tables",   nullptr},
     {"no-hinting", 0, 0, G_OPTION_ARG_NONE,  &this->drop_hints,   "Whether to drop hints",   nullptr},
+    {"retain-gids", 0, 0, G_OPTION_ARG_NONE,  &this->retain_gids,   "If set don't renumber glyph ids in the subset.",   nullptr},
     {"desubroutinize", 0, 0, G_OPTION_ARG_NONE,  &this->desubroutinize,   "Remove CFF/CFF2 use of subroutines",   nullptr},
 
     {nullptr}
diff --git a/util/options.hh b/util/options.hh
index e846258..84139f5 100644
--- a/util/options.hh
+++ b/util/options.hh
@@ -675,6 +675,7 @@
   {
     keep_layout = false;
     drop_hints = false;
+    retain_gids = false;
     desubroutinize = false;
 
     add_options (parser);
@@ -684,6 +685,7 @@
 
   hb_bool_t keep_layout;
   hb_bool_t drop_hints;
+  hb_bool_t retain_gids;
   hb_bool_t desubroutinize;
 };