[HarfBuzz] harfbuzz: Branch 'master' - 20 commits

Behdad Esfahbod behdad at kemper.freedesktop.org
Sat Feb 10 19:25:59 UTC 2018


 src/hb-open-file-private.hh        |   12 ++
 src/hb-open-type-private.hh        |    7 -
 src/hb-ot-cmap-table.hh            |  122 +++++++++++++++++++++++---
 src/hb-ot-layout-common-private.hh |    4 
 src/hb-ot-layout-gsub-table.hh     |   10 +-
 src/hb-private.hh                  |   64 ++++++++-----
 src/hb-subset-glyf.cc              |   67 ++++++++++----
 src/hb-subset-glyf.hh              |    1 
 src/hb-subset-plan.cc              |  100 +++++++++++++++------
 src/hb-subset-plan.hh              |    9 +
 src/hb-subset.cc                   |  173 +++++++++++++++++++++++--------------
 util/hb-subset.cc                  |    1 
 12 files changed, 410 insertions(+), 160 deletions(-)

New commits:
commit af274507c4f4c5a582543affa71d81a87d6d9151
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Sat Feb 10 13:25:49 2018 -0600

    Minor

diff --git a/src/hb-private.hh b/src/hb-private.hh
index 4d526d9d..4a8d950d 100644
--- a/src/hb-private.hh
+++ b/src/hb-private.hh
@@ -425,14 +425,13 @@ struct hb_prealloced_array_t
     return &array[len - 1];
   }
 
-  // Alloc enouch for size if size < allocated. Don't adjust len.
+  /* Allocate for size but don't adjust len. */
   inline bool alloc(unsigned int size)
   {
-    if (likely (size <= allocated)) 
-    {
+    if (likely (size <= allocated))
       return true;
-    }
-    /* Need to reallocate */
+
+    /* Reallocate */
 
     unsigned int new_allocated = allocated;
     while (size >= new_allocated)
@@ -456,16 +455,14 @@ struct hb_prealloced_array_t
 
     array = new_array;
     allocated = new_allocated;
-    
+
     return true;
   }
 
   inline bool resize (unsigned int size)
   {
-    if (!alloc(size)) 
-    {
+    if (!alloc (size))
       return false;
-    }
 
     len = size;
     return true;
commit 570d523761b23a3c668d9071712d5f10944d21fc
Merge: 71130a20 d18decd2
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Sat Feb 10 13:24:22 2018 -0600

    [subset] Merge remote-tracking branch 'googlefonts/master'

commit 71130a20fae7c256b0ab1aa397cc1ac2d11dd487
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Sat Feb 10 13:15:57 2018 -0600

    Replace Supplier.advance with Supplier::operator+=

diff --git a/src/hb-open-file-private.hh b/src/hb-open-file-private.hh
index ab168ab8..39399052 100644
--- a/src/hb-open-file-private.hh
+++ b/src/hb-open-file-private.hh
@@ -151,8 +151,8 @@ typedef struct OffsetTable
       if (rec.length % 4)
 	p = c->allocate_size<void> (4 - rec.length % 4);
     }
-    tags.advance (table_count);
-    blobs.advance (table_count);
+    tags += table_count;
+    blobs += table_count;
 
     /* TODO: update head table checkSumAdjustment. */
 
diff --git a/src/hb-open-type-private.hh b/src/hb-open-type-private.hh
index 3a8f738b..ef49ea91 100644
--- a/src/hb-open-type-private.hh
+++ b/src/hb-open-type-private.hh
@@ -511,12 +511,13 @@ struct Supplier
     return * (const Type *) (const void *) ((const char *) head + stride * i);
   }
 
-  inline void advance (unsigned int count)
+  inline Supplier<Type> & operator += (unsigned int count)
   {
     if (unlikely (count > len))
       count = len;
     len -= count;
     head = (const Type *) (const void *) ((const char *) head + stride * count);
+    return *this;
   }
 
   private:
@@ -883,7 +884,7 @@ struct ArrayOf
     if (unlikely (!serialize (c, items_len))) return_trace (false);
     for (unsigned int i = 0; i < items_len; i++)
       array[i] = items[i];
-    items.advance (items_len);
+    items += items_len;
     return_trace (true);
   }
 
@@ -1006,7 +1007,7 @@ struct HeadlessArrayOf
     if (unlikely (!c->extend (*this))) return_trace (false);
     for (unsigned int i = 0; i < items_len - 1; i++)
       array[i] = items[i];
-    items.advance (items_len - 1);
+    items += items_len - 1;
     return_trace (true);
   }
 
diff --git a/src/hb-ot-layout-common-private.hh b/src/hb-ot-layout-common-private.hh
index 8bf69a37..cb66c81a 100644
--- a/src/hb-ot-layout-common-private.hh
+++ b/src/hb-ot-layout-common-private.hh
@@ -700,7 +700,7 @@ struct CoverageFormat1
     if (unlikely (!c->extend (glyphArray))) return_trace (false);
     for (unsigned int i = 0; i < num_glyphs; i++)
       glyphArray[i] = glyphs[i];
-    glyphs.advance (num_glyphs);
+    glyphs += num_glyphs;
     return_trace (true);
   }
 
@@ -789,7 +789,7 @@ struct CoverageFormat2
       } else {
         rangeRecord[range].end = glyphs[i];
       }
-    glyphs.advance (num_glyphs);
+    glyphs += num_glyphs;
     return_trace (true);
   }
 
diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh
index 263e0a6e..97f1d21a 100644
--- a/src/hb-ot-layout-gsub-table.hh
+++ b/src/hb-ot-layout-gsub-table.hh
@@ -386,7 +386,7 @@ struct MultipleSubstFormat1
       if (unlikely (!sequence[i].serialize (c, this).serialize (c,
 								substitute_glyphs_list,
 								substitute_len_list[i]))) return_trace (false);
-    substitute_len_list.advance (num_glyphs);
+    substitute_len_list += num_glyphs;
     if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return_trace (false);
     return_trace (true);
   }
@@ -536,7 +536,7 @@ struct AlternateSubstFormat1
       if (unlikely (!alternateSet[i].serialize (c, this).serialize (c,
 								    alternate_glyphs_list,
 								    alternate_len_list[i]))) return_trace (false);
-    alternate_len_list.advance (num_glyphs);
+    alternate_len_list += num_glyphs;
     if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return_trace (false);
     return_trace (true);
   }
@@ -757,8 +757,8 @@ struct LigatureSet
 								ligatures[i],
 								component_list,
 								component_count_list[i]))) return_trace (false);
-    ligatures.advance (num_ligatures);
-    component_count_list.advance (num_ligatures);
+    ligatures += num_ligatures;
+    component_count_list += num_ligatures;
     return_trace (true);
   }
 
@@ -850,7 +850,7 @@ struct LigatureSubstFormat1
 								   component_count_list,
 								   ligature_per_first_glyph_count_list[i],
 								   component_list))) return_trace (false);
-    ligature_per_first_glyph_count_list.advance (num_first_glyphs);
+    ligature_per_first_glyph_count_list += num_first_glyphs;
     if (unlikely (!coverage.serialize (c, this).serialize (c, first_glyphs, num_first_glyphs))) return_trace (false);
     return_trace (true);
   }
commit d18decd2013f24f315dbd3b15cdd80c5a734e7e9
Author: Garret Rieger <grieger at google.com>
Date:   Fri Feb 9 18:41:21 2018 -0800

    In glyf subsetting add suport for writing out a short loca table when possible.

diff --git a/src/hb-subset-glyf.cc b/src/hb-subset-glyf.cc
index a186cdf7..b0f44e2c 100644
--- a/src/hb-subset-glyf.cc
+++ b/src/hb-subset-glyf.cc
@@ -32,7 +32,8 @@
 bool
 _calculate_glyf_and_loca_prime_size (const OT::glyf::accelerator_t &glyf,
                                      hb_auto_array_t<unsigned int> &glyph_ids,
-                                     unsigned int *glyf_size /* OUT */,
+                                     bool *use_short_loca, /* OUT */
+                                     unsigned int *glyf_size, /* OUT */
                                      unsigned int *loca_size /* OUT */)
 {
   unsigned int total = 0;
@@ -51,23 +52,37 @@ _calculate_glyf_and_loca_prime_size (const OT::glyf::accelerator_t &glyf,
   }
 
   *glyf_size = total;
-  *loca_size = (count + 1) * sizeof(OT::HBUINT32);
+  *use_short_loca = (total <= 131070);
+  *loca_size = (count + 1)
+      * (*use_short_loca ? 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");
   return true;
 }
 
+void
+_write_loca_entry (unsigned int id, unsigned int offset, bool is_short, void *loca_prime) {
+  if (is_short) {
+    ((OT::HBUINT16*) loca_prime) [id].set (offset / 2);
+  } else {
+    ((OT::HBUINT32*) loca_prime) [id].set (offset);
+  }
+}
+
 bool
 _write_glyf_and_loca_prime (const OT::glyf::accelerator_t &glyf,
                             const char                    *glyf_data,
                             hb_auto_array_t<unsigned int> &glyph_ids,
+                            bool                           use_short_loca,
                             int                            glyf_prime_size,
                             char                          *glyf_prime_data /* OUT */,
                             int                            loca_prime_size,
                             char                          *loca_prime_data /* OUT */)
 {
-  // TODO(grieger): Handle the missing character glyf and outline.
-
   char *glyf_prime_data_next = glyf_prime_data;
-  OT::HBUINT32 *loca_prime = (OT::HBUINT32*) loca_prime_data;
 
   hb_codepoint_t next_glyph = -1;
   hb_codepoint_t new_glyph_id = 0;
@@ -81,7 +96,8 @@ _write_glyf_and_loca_prime (const OT::glyf::accelerator_t &glyf,
 
     int length = end_offset - start_offset;
     memcpy (glyf_prime_data_next, glyf_data + start_offset, length);
-    loca_prime[new_glyph_id].set(start_offset);
+
+    _write_loca_entry (i, start_offset, use_short_loca, loca_prime_data);
 
     glyf_prime_data_next += length;
     new_glyph_id++;
@@ -89,7 +105,7 @@ _write_glyf_and_loca_prime (const OT::glyf::accelerator_t &glyf,
 
   // Add the last loca entry which doesn't correspond to a specific glyph
   // but identifies the end of the last glyphs data.
-  loca_prime[new_glyph_id].set(end_offset);
+  _write_loca_entry (new_glyph_id, end_offset, use_short_loca, loca_prime_data);
 
   return true;
 }
@@ -98,18 +114,20 @@ bool
 _hb_subset_glyf_and_loca (const OT::glyf::accelerator_t  &glyf,
                           const char                     *glyf_data,
                           hb_auto_array_t<unsigned int>  &glyphs_to_retain,
+                          bool                           *use_short_loca,
                           hb_blob_t                     **glyf_prime /* OUT */,
                           hb_blob_t                     **loca_prime /* OUT */)
 {
   // TODO(grieger): Sanity check writes to make sure they are in-bounds.
   // TODO(grieger): Sanity check allocation size for the new table.
   // TODO(grieger): Don't fail on bad offsets, just dump them.
-  // TODO(grieger): Support short loca output.
 
   unsigned int glyf_prime_size;
   unsigned int loca_prime_size;
+
   if (unlikely (!_calculate_glyf_and_loca_prime_size (glyf,
                                                       glyphs_to_retain,
+                                                      use_short_loca,
                                                       &glyf_prime_size,
                                                       &loca_prime_size))) {
     return false;
@@ -118,6 +136,7 @@ _hb_subset_glyf_and_loca (const OT::glyf::accelerator_t  &glyf,
   char *glyf_prime_data = (char *) calloc (glyf_prime_size, 1);
   char *loca_prime_data = (char *) calloc (loca_prime_size, 1);
   if (unlikely (!_write_glyf_and_loca_prime (glyf, glyf_data, glyphs_to_retain,
+                                             *use_short_loca,
                                              glyf_prime_size, glyf_prime_data,
                                              loca_prime_size, loca_prime_data))) {
     free (glyf_prime_data);
@@ -157,7 +176,12 @@ hb_subset_glyf_and_loca (hb_subset_plan_t *plan,
 
   OT::glyf::accelerator_t glyf;
   glyf.init(face);
-  bool result = _hb_subset_glyf_and_loca (glyf, glyf_data, plan->gids_to_retain_sorted, glyf_prime, loca_prime);
+  bool result = _hb_subset_glyf_and_loca (glyf,
+                                          glyf_data,
+                                          plan->gids_to_retain_sorted,
+                                          use_short_loca,
+                                          glyf_prime,
+                                          loca_prime);
   glyf.fini();
 
   *use_short_loca = false;
commit 9275bd03ea427eb607dde6a8e65f78a350b88323
Author: Rod Sheeter <rsheeter at google.com>
Date:   Fri Feb 9 17:33:34 2018 -0800

    First pass at building a cmap

diff --git a/src/hb-ot-cmap-table.hh b/src/hb-ot-cmap-table.hh
index bb3eba47..daee6ca5 100644
--- a/src/hb-ot-cmap-table.hh
+++ b/src/hb-ot-cmap-table.hh
@@ -254,6 +254,8 @@ struct CmapSubtableFormat10 : CmapSubtableTrimmed<HBUINT32 > {};
 template <typename T>
 struct CmapSubtableLongSegmented
 {
+  friend struct cmap;
+
   inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
   {
     int i = groups.bsearch (codepoint);
@@ -269,6 +271,20 @@ struct CmapSubtableLongSegmented
     return_trace (c->check_struct (this) && groups.sanitize (c));
   }
 
+  inline bool serialize(hb_serialize_context_t *context,
+                        unsigned int group_count,
+                        Supplier<CmapSubtableLongGroup> &group_supplier)
+  {
+    TRACE_SERIALIZE (this);
+    if (unlikely(!context->extend_min (*this))) return_trace (false);
+    if (unlikely(!groups.serialize(context, group_count))) return_trace (false);
+    for (unsigned int i = 0; i < group_count; i++) {
+      const CmapSubtableLongGroup &group = group_supplier[i];
+      memcpy(&groups[i], &group, sizeof(group));
+    }
+    return true;
+  }
+
   protected:
   HBUINT16	format;		/* Subtable format; set to 12. */
   HBUINT16	reservedZ;	/* Reserved; set to 0. */
@@ -505,15 +521,15 @@ struct cmap
 		  encodingRecord.sanitize (c, this));
   }
 
-  inline bool subset (hb_subset_plan_t *plan, hb_face_t *source, hb_face_t *dest) const
+  inline void populate_groups(hb_auto_array_t<hb_codepoint_t> &codepoints,
+                              hb_auto_array_t<CmapSubtableLongGroup> *groups) const
   {
-    hb_auto_array_t<CmapSubtableLongGroup> groups;
     CmapSubtableLongGroup *group = nullptr;
-    for (unsigned int i = 0; i < plan->codepoints.len; i++) {
-      hb_codepoint_t cp = plan->codepoints[i];
+    for (unsigned int i = 0; i < codepoints.len; i++) {
+      hb_codepoint_t cp = codepoints[i];
       if (!group)
       {
-        group = groups.push();
+        group = groups->push();
         group->startCharCode.set(cp);
         group->endCharCode.set(cp);
         group->glyphID.set(i);  // index in codepoints is new gid
@@ -527,13 +543,84 @@ struct cmap
     }
 
     DEBUG_MSG(SUBSET, nullptr, "cmap");
-    for (unsigned int i = 0; i < groups.len; i++) {
-      CmapSubtableLongGroup& group = groups[i];
-      DEBUG_MSG(SUBSET, nullptr, "  %d: U+%04X-U+%04X, first gid %d", i, (uint32_t) group.startCharCode, (uint32_t) group.endCharCode, (uint32_t) group.glyphID);
+    for (unsigned int i = 0; i < groups->len; i++) {
+      CmapSubtableLongGroup& group = (*groups)[i];
+      DEBUG_MSG(SUBSET, nullptr, "  %d: U+%04X-U+%04X, gid %d-%d", i, (uint32_t) group.startCharCode, (uint32_t) group.endCharCode, (uint32_t) group.glyphID, (uint32_t) group.glyphID + ((uint32_t) group.endCharCode - (uint32_t) group.startCharCode));
+    }
+  }
+
+  hb_bool_t _subset (hb_auto_array_t<CmapSubtableLongGroup> &groups,
+                     size_t dest_sz,
+                     void *dest) const
+  {
+    hb_serialize_context_t context(dest, dest_sz);
+
+    OT::cmap *cmap = context.start_serialize<OT::cmap> ();
+    if (unlikely(!context.extend_min(*cmap)))
+    {
+      return false;
+    }
+
+    cmap->version.set(0);
+
+    if (unlikely(!cmap->encodingRecord.serialize(&context, /* numTables */ 1)))
+    {
+      return false;
     }
+
+    EncodingRecord &rec = cmap->encodingRecord[0];
+    rec.platformID.set (3); // Windows
+    rec.encodingID.set (1); // Unicode BMP
+
+    CmapSubtable &subtable = rec.subtable.serialize(&context, &rec.subtable);
+    subtable.u.format.set(12);
+
+    CmapSubtableFormat12 &format12 = subtable.u.format12;
+    format12.format.set(12);
+    format12.reservedZ.set(0);
+
+    OT::Supplier<CmapSubtableLongGroup> group_supplier  (&groups[0], groups.len, sizeof (CmapSubtableLongGroup));
+    if (unlikely(!format12.serialize(&context, groups.len, group_supplier)))
+    {
+      return false;
+    }
+
+    context.end_serialize ();
     return true;
   }
 
+  hb_blob_t * subset (hb_subset_plan_t *plan, hb_face_t *source) const
+  {
+    hb_auto_array_t<CmapSubtableLongGroup> groups;
+
+    populate_groups(plan->codepoints, &groups);
+
+    // We now know how big our blob needs to be
+    // TODO use APIs from the structs to get size?
+    size_t dest_sz = 4 // header
+                   + 8 // 1 EncodingRecord
+                   + 16 // Format 12 header
+                   + 12 * groups.len; // SequentialMapGroup records
+    void *dest = calloc(dest_sz, 1);
+    if (unlikely(!dest)) {
+      DEBUG_MSG(SUBSET, nullptr, "Unable to alloc %ld for cmap subset output", dest_sz);
+      return nullptr;
+    }
+
+    if (unlikely(!_subset(groups, dest_sz, dest)))
+    {
+      free(dest);
+      return nullptr;
+    }
+
+    // all done, write the blob into dest
+    return hb_blob_create((const char *)dest, 
+                          dest_sz,
+                          HB_MEMORY_MODE_READONLY,
+                          /* userdata */ nullptr,
+                          free);
+  }
+
   struct accelerator_t
   {
     inline void init (hb_face_t *face)
diff --git a/src/hb-subset.cc b/src/hb-subset.cc
index 98565c30..8e1f8198 100644
--- a/src/hb-subset.cc
+++ b/src/hb-subset.cc
@@ -108,19 +108,19 @@ hb_subset_input_destroy(hb_subset_input_t *subset_input)
 }
 
 template<typename TableType>
-hb_bool_t
-subset (hb_subset_plan_t *plan, hb_face_t *source, hb_face_t *dest)
+hb_blob_t *
+_subset (hb_subset_plan_t *plan, hb_face_t *source)
 {
     OT::Sanitizer<TableType> sanitizer;
-    hb_blob_t *table_blob = sanitizer.sanitize (source->reference_table (TableType::tableTag));
-    if (unlikely(!table_blob)) {
+    hb_blob_t *source_blob = sanitizer.sanitize (source->reference_table (TableType::tableTag));
+    if (unlikely(!source_blob)) {
       DEBUG_MSG(SUBSET, nullptr, "Failed to reference table for tag %d", TableType::tableTag);
-      return false;
+      return nullptr;
     }
-    const TableType *table = OT::Sanitizer<TableType>::lock_instance (table_blob);
-    hb_bool_t result = table->subset(plan, source, dest);
+    const TableType *table = OT::Sanitizer<TableType>::lock_instance (source_blob);
+    hb_blob_t *result = table->subset(plan, source);
 
-    hb_blob_destroy (table_blob);
+    hb_blob_destroy (source_blob);
 
     hb_tag_t tag = TableType::tableTag;
     DEBUG_MSG(SUBSET, nullptr, "Subset %c%c%c%c %s", HB_UNTAG(tag), result ? "success" : "FAILED!");
@@ -242,7 +242,6 @@ hb_subset_face_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob)
     return false;
 
   hb_subset_face_data_t *data = (hb_subset_face_data_t *) face->user_data;
-
   hb_subset_face_data_t::table_entry_t *entry = data->tables.push ();
   if (unlikely (!entry))
     return false;
@@ -306,10 +305,12 @@ bool
 _subset_table (hb_subset_plan_t *plan,
                hb_face_t        *source,
                hb_tag_t          tag,
-               hb_blob_t        *table_blob,
+               hb_blob_t        *source_blob,
                hb_face_t        *dest)
 {
   // TODO (grieger): Handle updating the head table (loca format + num glyphs)
+  DEBUG_MSG(SUBSET, nullptr, "begin subset %c%c%c%c", HB_UNTAG(tag));
+  hb_blob_t *dest_blob;
   switch (tag) {
     case HB_OT_TAG_glyf:
       return _subset_glyf (plan, source, dest);
@@ -320,14 +321,16 @@ _subset_table (hb_subset_plan_t *plan,
       // SKIP loca, it's handle by glyf
       return true;
     case HB_OT_TAG_cmap:
-      // TODO(rsheeter): remove hb_subset_face_add_table
-      //                 once cmap subsetting works.
-      hb_subset_face_add_table (dest, tag, table_blob);
-      return subset<const OT::cmap> (plan, source, dest);
+      dest_blob = _subset<const OT::cmap> (plan, source);
+      break;
     default:
-      // Default action, copy table as is.
-      return hb_subset_face_add_table (dest, tag, table_blob);
-  }
+      dest_blob = source_blob;
+      break;
+  } 
+  DEBUG_MSG(SUBSET, nullptr, "subset %c%c%c%c %s", HB_UNTAG(tag), dest_blob ? "ok" : "FAILED");
+  if (unlikely(!dest_blob)) return false;
+  if (unlikely(!hb_subset_face_add_table (dest, tag, dest_blob))) return false;
+  return true;
 }
 
 /**
commit d2170d14780ad6f8e0d17a1e011445c3bcc20871
Author: Garret Rieger <grieger at google.com>
Date:   Fri Feb 9 17:24:16 2018 -0800

    Check for failures from add table.

diff --git a/src/hb-subset.cc b/src/hb-subset.cc
index 4eaf188c..98565c30 100644
--- a/src/hb-subset.cc
+++ b/src/hb-subset.cc
@@ -270,7 +270,7 @@ _add_head_and_set_loca_version (hb_face_t *source, bool use_short_loca, hb_face_
                                                  HB_MEMORY_MODE_WRITABLE,
                                                  head_prime,
                                                  free);
-    hb_subset_face_add_table (dest, HB_OT_TAG_head, head_prime_blob);
+    has_head = has_head && hb_subset_face_add_table (dest, HB_OT_TAG_head, head_prime_blob);
 
     hb_blob_destroy (head_prime_blob);
   }
@@ -290,8 +290,8 @@ _subset_glyf (hb_subset_plan_t *plan, hb_face_t *source, hb_face_t *dest)
   bool use_short_loca = false;
   // TODO(grieger): Migrate to subset function on the table like cmap.
   if (hb_subset_glyf_and_loca (plan, source, &use_short_loca, &glyf_prime, &loca_prime)) {
-    hb_subset_face_add_table (dest, HB_OT_TAG_glyf, glyf_prime);
-    hb_subset_face_add_table (dest, HB_OT_TAG_loca, loca_prime);
+    success = success && hb_subset_face_add_table (dest, HB_OT_TAG_glyf, glyf_prime);
+    success = success && hb_subset_face_add_table (dest, HB_OT_TAG_loca, loca_prime);
     success = success && _add_head_and_set_loca_version (source, use_short_loca, dest);
   } else {
     success = false;
@@ -326,8 +326,7 @@ _subset_table (hb_subset_plan_t *plan,
       return subset<const OT::cmap> (plan, source, dest);
     default:
       // Default action, copy table as is.
-      hb_subset_face_add_table (dest, tag, table_blob);
-      return true;
+      return hb_subset_face_add_table (dest, tag, table_blob);
   }
 }
 
commit 4816064c0e5464d032a55001a959a9abcef7f70e
Author: Rod Sheeter <rsheeter at google.com>
Date:   Fri Feb 9 17:14:37 2018 -0800

    add missing return

diff --git a/src/hb-private.hh b/src/hb-private.hh
index a3d1250a..751dec60 100644
--- a/src/hb-private.hh
+++ b/src/hb-private.hh
@@ -448,7 +448,9 @@ struct hb_prealloced_array_t
       return false;
 
     array = new_array;
-    allocated = new_allocated;    
+    allocated = new_allocated;
+    
+    return true;
   }
 
   inline bool resize (unsigned int size)
commit 0089443756cdcef0182e55cf8480b96a64d31cc7
Author: Garret Rieger <grieger at google.com>
Date:   Fri Feb 9 16:22:09 2018 -0800

    Keep a second set of glyph ids in subset plan which is sorted by glyph id and always has gid 0

diff --git a/src/hb-subset-glyf.cc b/src/hb-subset-glyf.cc
index c6e2b5db..a186cdf7 100644
--- a/src/hb-subset-glyf.cc
+++ b/src/hb-subset-glyf.cc
@@ -157,7 +157,7 @@ hb_subset_glyf_and_loca (hb_subset_plan_t *plan,
 
   OT::glyf::accelerator_t glyf;
   glyf.init(face);
-  bool result = _hb_subset_glyf_and_loca (glyf, glyf_data, plan->gids_to_retain, glyf_prime, loca_prime);
+  bool result = _hb_subset_glyf_and_loca (glyf, glyf_data, plan->gids_to_retain_sorted, glyf_prime, loca_prime);
   glyf.fini();
 
   *use_short_loca = false;
diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc
index 4cb4b7c9..fb8913e8 100644
--- a/src/hb-subset-plan.cc
+++ b/src/hb-subset-plan.cc
@@ -30,7 +30,7 @@
 #include "hb-ot-cmap-table.hh"
 
 int
-hb_codepoint_t_cmp (const void *l, const void *r) {
+_hb_codepoint_t_cmp (const void *l, const void *r) {
   return *((hb_codepoint_t *) l) - *((hb_codepoint_t *) r);
 }
 
@@ -40,8 +40,8 @@ hb_subset_plan_new_gid_for_old_id (hb_subset_plan_t *plan,
                                    hb_codepoint_t *new_gid)
 {
   // the index in old_gids is the new gid; only up to codepoints.len are valid
-  for (unsigned int i = 0; i < plan->codepoints.len; i++) {
-    if (plan->gids_to_retain[i] == old_gid) {
+  for (unsigned int i = 0; i < plan->gids_to_retain_sorted.len; i++) {
+    if (plan->gids_to_retain_sorted[i] == old_gid) {
       *new_gid = i;
       return true;
     }
@@ -59,13 +59,14 @@ _populate_codepoints (hb_set_t *input_codepoints,
     hb_codepoint_t *wr = plan_codepoints.push();
     *wr = cp;
   }
-  plan_codepoints.qsort (hb_codepoint_t_cmp);
+  plan_codepoints.qsort (_hb_codepoint_t_cmp);
 }
 
 void
 _populate_gids_to_retain (hb_face_t *face,
                           hb_auto_array_t<hb_codepoint_t>& codepoints,
-                          hb_auto_array_t<hb_codepoint_t>& old_gids)
+                          hb_auto_array_t<hb_codepoint_t>& old_gids,
+                          hb_auto_array_t<hb_codepoint_t>& old_gids_sorted)
 {
   OT::cmap::accelerator_t cmap;
   cmap.init (face);
@@ -73,12 +74,16 @@ _populate_gids_to_retain (hb_face_t *face,
   hb_auto_array_t<unsigned int> bad_indices;
 
   old_gids.alloc (codepoints.len);
+  bool has_zero = false;
   for (unsigned int i = 0; i < codepoints.len; i++) {
     hb_codepoint_t gid;
     if (!cmap.get_nominal_glyph (codepoints[i], &gid)) {
       gid = -1;
       *(bad_indices.push ()) = i;
     }
+    if (gid == 0) {
+      has_zero = true;
+    }
     *(old_gids.push ()) = gid;
   }
 
@@ -90,13 +95,20 @@ _populate_gids_to_retain (hb_face_t *face,
     old_gids.remove (i);
   }
 
+  // Populate a second glyph id array that is sorted by glyph id
+  // and is gauranteed to contain 0.
+  old_gids_sorted.alloc (old_gids.len + (has_zero ? 0 : 1));
+  for (unsigned int i = 0; i < old_gids.len; i++) {
+    *(old_gids_sorted.push ()) = old_gids[i];
+  }
+  if (!has_zero)
+    *(old_gids_sorted.push ()) = 0;
+  old_gids_sorted.qsort (_hb_codepoint_t_cmp);
+
   for (unsigned int i = 0; i < codepoints.len; i++) {
       DEBUG_MSG(SUBSET, nullptr, " U+%04X, old_gid %d, new_gid %d", codepoints[i], old_gids[i], i);
   }
 
-  // TODO always keep .notdef
-
-
   // TODO(Q1) expand with glyphs that make up complex glyphs
   // TODO expand with glyphs reached by G*
   //
@@ -120,7 +132,10 @@ hb_subset_plan_create (hb_face_t           *face,
 {
   hb_subset_plan_t *plan = hb_object_create<hb_subset_plan_t> ();
   _populate_codepoints (input->codepoints, plan->codepoints);
-  _populate_gids_to_retain (face, plan->codepoints, plan->gids_to_retain);
+  _populate_gids_to_retain (face,
+                            plan->codepoints,
+                            plan->gids_to_retain,
+                            plan->gids_to_retain_sorted);
   return plan;
 }
 
@@ -143,5 +158,6 @@ hb_subset_plan_destroy (hb_subset_plan_t *plan)
 
   plan->codepoints.finish ();
   plan->gids_to_retain.finish ();
+  plan->gids_to_retain_sorted.finish ();
   free (plan);
 }
diff --git a/src/hb-subset-plan.hh b/src/hb-subset-plan.hh
index c7e9108c..e1c3bd3e 100644
--- a/src/hb-subset-plan.hh
+++ b/src/hb-subset-plan.hh
@@ -40,6 +40,7 @@ struct hb_subset_plan_t {
   // codepoints is sorted and aligned with gids_to_retain.
   hb_auto_array_t<hb_codepoint_t> codepoints;
   hb_auto_array_t<hb_codepoint_t> gids_to_retain;
+  hb_auto_array_t<hb_codepoint_t> gids_to_retain_sorted;
 };
 
 typedef struct hb_subset_plan_t hb_subset_plan_t;
commit 3bc81558d836e27e77bda0d6da9c71f530719579
Author: Garret Rieger <grieger at google.com>
Date:   Fri Feb 9 16:06:33 2018 -0800

    Formatting for hb-subset-plan.

diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc
index 9dbc5a09..4cb4b7c9 100644
--- a/src/hb-subset-plan.cc
+++ b/src/hb-subset-plan.cc
@@ -29,15 +29,16 @@
 #include "hb-subset-plan.hh"
 #include "hb-ot-cmap-table.hh"
 
-int hb_codepoint_t_cmp(const void *l, const void *r) {
+int
+hb_codepoint_t_cmp (const void *l, const void *r) {
   return *((hb_codepoint_t *) l) - *((hb_codepoint_t *) r);
 }
 
 hb_bool_t
-hb_subset_plan_new_gid_for_old_id(hb_subset_plan_t *plan,
-                                  hb_codepoint_t old_gid,
-                                  hb_codepoint_t *new_gid) {
-
+hb_subset_plan_new_gid_for_old_id (hb_subset_plan_t *plan,
+                                   hb_codepoint_t old_gid,
+                                   hb_codepoint_t *new_gid)
+{
   // the index in old_gids is the new gid; only up to codepoints.len are valid
   for (unsigned int i = 0; i < plan->codepoints.len; i++) {
     if (plan->gids_to_retain[i] == old_gid) {
@@ -48,43 +49,45 @@ hb_subset_plan_new_gid_for_old_id(hb_subset_plan_t *plan,
   return false;
 }
 
-void populate_codepoints(hb_set_t *input_codepoints,
-                         hb_auto_array_t<hb_codepoint_t>& plan_codepoints) {
-  plan_codepoints.alloc(hb_set_get_population(input_codepoints));
+void
+_populate_codepoints (hb_set_t *input_codepoints,
+                      hb_auto_array_t<hb_codepoint_t>& plan_codepoints)
+{
+  plan_codepoints.alloc (hb_set_get_population (input_codepoints));
   hb_codepoint_t cp = -1;
-  while (hb_set_next(input_codepoints, &cp)) {
+  while (hb_set_next (input_codepoints, &cp)) {
     hb_codepoint_t *wr = plan_codepoints.push();
     *wr = cp;
   }
-  plan_codepoints.qsort(hb_codepoint_t_cmp);
+  plan_codepoints.qsort (hb_codepoint_t_cmp);
 }
 
 void
-populate_gids_to_retain (hb_face_t *face,
-                         hb_auto_array_t<hb_codepoint_t>& codepoints,
-                         hb_auto_array_t<hb_codepoint_t>& old_gids)
+_populate_gids_to_retain (hb_face_t *face,
+                          hb_auto_array_t<hb_codepoint_t>& codepoints,
+                          hb_auto_array_t<hb_codepoint_t>& old_gids)
 {
   OT::cmap::accelerator_t cmap;
   cmap.init (face);
 
   hb_auto_array_t<unsigned int> bad_indices;
 
-  old_gids.alloc(codepoints.len);
+  old_gids.alloc (codepoints.len);
   for (unsigned int i = 0; i < codepoints.len; i++) {
     hb_codepoint_t gid;
-    if (!cmap.get_nominal_glyph(codepoints[i], &gid)) {
+    if (!cmap.get_nominal_glyph (codepoints[i], &gid)) {
       gid = -1;
-      *(bad_indices.push()) = i;
+      *(bad_indices.push ()) = i;
     }
-    *(old_gids.push()) = gid;
+    *(old_gids.push ()) = gid;
   }
 
   while (bad_indices.len > 0) {
     unsigned int i = bad_indices[bad_indices.len - 1];
-    bad_indices.pop();
+    bad_indices.pop ();
     DEBUG_MSG(SUBSET, nullptr, "Drop U+%04X; no gid", codepoints[i]);
-    codepoints.remove(i);
-    old_gids.remove(i);
+    codepoints.remove (i);
+    old_gids.remove (i);
   }
 
   for (unsigned int i = 0; i < codepoints.len; i++) {
@@ -116,8 +119,8 @@ hb_subset_plan_create (hb_face_t           *face,
                        hb_subset_input_t   *input)
 {
   hb_subset_plan_t *plan = hb_object_create<hb_subset_plan_t> ();
-  populate_codepoints(input->codepoints, plan->codepoints);
-  populate_gids_to_retain(face, plan->codepoints, plan->gids_to_retain);
+  _populate_codepoints (input->codepoints, plan->codepoints);
+  _populate_gids_to_retain (face, plan->codepoints, plan->gids_to_retain);
   return plan;
 }
 
@@ -138,7 +141,7 @@ hb_subset_plan_destroy (hb_subset_plan_t *plan)
 {
   if (!hb_object_destroy (plan)) return;
 
-  plan->codepoints.finish();
-  plan->gids_to_retain.finish();
+  plan->codepoints.finish ();
+  plan->gids_to_retain.finish ();
   free (plan);
 }
commit 86aa4b3ba7cd075f01614874dae88a771b8c54fd
Author: Garret Rieger <grieger at google.com>
Date:   Fri Feb 9 13:54:43 2018 -0800

    Return empty face on hb_subset failure instead of null. Plus some minor cleanups for _add_head_and_set_loca_version

diff --git a/src/hb-subset.cc b/src/hb-subset.cc
index cf8ac6cc..4eaf188c 100644
--- a/src/hb-subset.cc
+++ b/src/hb-subset.cc
@@ -258,8 +258,9 @@ _add_head_and_set_loca_version (hb_face_t *source, bool use_short_loca, hb_face_
 {
   hb_blob_t *head_blob = OT::Sanitizer<OT::head>().sanitize (hb_face_reference_table (source, HB_OT_TAG_head));
   const OT::head *head = OT::Sanitizer<OT::head>::lock_instance (head_blob);
+  bool has_head = (head != nullptr);
 
-  if (head) {
+  if (has_head) {
     OT::head *head_prime = (OT::head *) calloc (OT::head::static_size, 1);
     memcpy (head_prime, head, OT::head::static_size);
     head_prime->indexToLocFormat.set (use_short_loca ? 0 : 1);
@@ -276,7 +277,7 @@ _add_head_and_set_loca_version (hb_face_t *source, bool use_short_loca, hb_face_
 
   hb_blob_destroy (head_blob);
 
-  return !head;
+  return has_head;
 }
 
 bool
@@ -298,8 +299,6 @@ _subset_glyf (hb_subset_plan_t *plan, hb_face_t *source, hb_face_t *dest)
   hb_blob_destroy (loca_prime);
   hb_blob_destroy (glyf_prime);
 
-  _add_head_and_set_loca_version (source, use_short_loca, dest);
-
   return success;
 }
 
@@ -345,7 +344,7 @@ hb_subset (hb_face_t *source,
 	   hb_subset_profile_t *profile,
            hb_subset_input_t *input)
 {
-  if (unlikely (!profile || !input || !source)) return nullptr;
+  if (unlikely (!profile || !input || !source)) return hb_face_get_empty();
 
   hb_subset_plan_t *plan = hb_subset_plan_create (source, profile, input);
 
@@ -368,5 +367,5 @@ hb_subset (hb_face_t *source,
   // TODO(grieger): Remove once basic subsetting is working + tests updated.
   hb_face_destroy (dest);
   hb_face_reference (source);
-  return success ? source : nullptr;
+  return success ? source : hb_face_get_empty();
 }
commit 1582eabee6017839518b821ef93a329a0a86a453
Author: Garret Rieger <grieger at google.com>
Date:   Fri Feb 9 12:52:08 2018 -0800

    Update head table with loca format selected by glyf+loca subsetting.

diff --git a/src/hb-subset-glyf.cc b/src/hb-subset-glyf.cc
index b7412fbb..c6e2b5db 100644
--- a/src/hb-subset-glyf.cc
+++ b/src/hb-subset-glyf.cc
@@ -103,10 +103,8 @@ _hb_subset_glyf_and_loca (const OT::glyf::accelerator_t  &glyf,
 {
   // TODO(grieger): Sanity check writes to make sure they are in-bounds.
   // TODO(grieger): Sanity check allocation size for the new table.
-  // TODO(grieger): Subset loca simultaneously.
   // TODO(grieger): Don't fail on bad offsets, just dump them.
   // TODO(grieger): Support short loca output.
-  // TODO(grieger): Add a extra loca entry at the end.
 
   unsigned int glyf_prime_size;
   unsigned int loca_prime_size;
@@ -150,7 +148,8 @@ _hb_subset_glyf_and_loca (const OT::glyf::accelerator_t  &glyf,
 bool
 hb_subset_glyf_and_loca (hb_subset_plan_t *plan,
                          hb_face_t        *face,
-                         hb_blob_t       **glyf_prime /* OUT */,
+                         bool             *use_short_loca, /* OUT */
+                         hb_blob_t       **glyf_prime, /* OUT */
                          hb_blob_t       **loca_prime /* OUT */)
 {
   hb_blob_t *glyf_blob = OT::Sanitizer<OT::glyf>().sanitize (face->reference_table (HB_OT_TAG_glyf));
@@ -161,7 +160,7 @@ hb_subset_glyf_and_loca (hb_subset_plan_t *plan,
   bool result = _hb_subset_glyf_and_loca (glyf, glyf_data, plan->gids_to_retain, glyf_prime, loca_prime);
   glyf.fini();
 
-  // TODO(grieger): Subset loca
+  *use_short_loca = false;
 
   return result;
 }
diff --git a/src/hb-subset-glyf.hh b/src/hb-subset-glyf.hh
index 035085f0..dbdd3410 100644
--- a/src/hb-subset-glyf.hh
+++ b/src/hb-subset-glyf.hh
@@ -34,6 +34,7 @@
 bool
 hb_subset_glyf_and_loca (hb_subset_plan_t *plan,
                          hb_face_t        *face,
+                         bool             *use_short_loca, /* OUT */
                          hb_blob_t       **glyf_prime /* OUT */,
                          hb_blob_t       **loca_prime /* OUT */);
 
diff --git a/src/hb-subset.cc b/src/hb-subset.cc
index 73812b72..cf8ac6cc 100644
--- a/src/hb-subset.cc
+++ b/src/hb-subset.cc
@@ -254,22 +254,52 @@ hb_subset_face_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob)
 }
 
 bool
+_add_head_and_set_loca_version (hb_face_t *source, bool use_short_loca, hb_face_t *dest)
+{
+  hb_blob_t *head_blob = OT::Sanitizer<OT::head>().sanitize (hb_face_reference_table (source, HB_OT_TAG_head));
+  const OT::head *head = OT::Sanitizer<OT::head>::lock_instance (head_blob);
+
+  if (head) {
+    OT::head *head_prime = (OT::head *) calloc (OT::head::static_size, 1);
+    memcpy (head_prime, head, OT::head::static_size);
+    head_prime->indexToLocFormat.set (use_short_loca ? 0 : 1);
+
+    hb_blob_t *head_prime_blob = hb_blob_create ((const char*) head_prime,
+                                                 OT::head::static_size,
+                                                 HB_MEMORY_MODE_WRITABLE,
+                                                 head_prime,
+                                                 free);
+    hb_subset_face_add_table (dest, HB_OT_TAG_head, head_prime_blob);
+
+    hb_blob_destroy (head_prime_blob);
+  }
+
+  hb_blob_destroy (head_blob);
+
+  return !head;
+}
+
+bool
 _subset_glyf (hb_subset_plan_t *plan, hb_face_t *source, hb_face_t *dest)
 {
   hb_blob_t *glyf_prime = nullptr;
   hb_blob_t *loca_prime = nullptr;
 
   bool success = true;
+  bool use_short_loca = false;
   // TODO(grieger): Migrate to subset function on the table like cmap.
-  if (hb_subset_glyf_and_loca (plan, source, &glyf_prime, &loca_prime)) {
+  if (hb_subset_glyf_and_loca (plan, source, &use_short_loca, &glyf_prime, &loca_prime)) {
     hb_subset_face_add_table (dest, HB_OT_TAG_glyf, glyf_prime);
     hb_subset_face_add_table (dest, HB_OT_TAG_loca, loca_prime);
+    success = success && _add_head_and_set_loca_version (source, use_short_loca, dest);
   } else {
     success = false;
   }
   hb_blob_destroy (loca_prime);
   hb_blob_destroy (glyf_prime);
 
+  _add_head_and_set_loca_version (source, use_short_loca, dest);
+
   return success;
 }
 
@@ -284,8 +314,11 @@ _subset_table (hb_subset_plan_t *plan,
   switch (tag) {
     case HB_OT_TAG_glyf:
       return _subset_glyf (plan, source, dest);
+    case HB_OT_TAG_head:
+      // SKIP head, it's handled by glyf
+      return true;
     case HB_OT_TAG_loca:
-      // SKIP loca, it's handle by the glyf subsetter.
+      // SKIP loca, it's handle by glyf
       return true;
     case HB_OT_TAG_cmap:
       // TODO(rsheeter): remove hb_subset_face_add_table
commit 335bbaa66f66e86d417cc123a2bf397e8b834f64
Author: Garret Rieger <grieger at google.com>
Date:   Fri Feb 9 10:55:15 2018 -0800

    Remove uneeded code in hb-subset.

diff --git a/src/hb-subset.cc b/src/hb-subset.cc
index 50bcac79..73812b72 100644
--- a/src/hb-subset.cc
+++ b/src/hb-subset.cc
@@ -316,25 +316,6 @@ hb_subset (hb_face_t *source,
 
   hb_subset_plan_t *plan = hb_subset_plan_create (source, profile, input);
 
-  hb_face_t *face = hb_subset_face_create ();
-
-  /* Copy tables to new face. */
-  {
-    hb_tag_t table_tags[32];
-    unsigned int offset = 0, count;
-    do {
-      count = ARRAY_LENGTH (table_tags);
-      hb_face_get_table_tags (source, offset, &count, table_tags);
-      for (unsigned int i = 0; i < count; i++)
-      {
-      	hb_tag_t tag = table_tags[i];
-      	hb_blob_t *blob = hb_face_reference_table (source, tag);
-      	hb_subset_face_add_table (face, tag, blob);
-      	hb_blob_destroy (blob);
-      }
-    } while (count == ARRAY_LENGTH (table_tags));
-  }
-
   hb_face_t *dest = hb_subset_face_create ();
   hb_tag_t table_tags[32];
   unsigned int offset = 0, count;
commit 1cd98d05e07498653ba60a68b6342d1a90429eba
Author: Rod Sheeter <rsheeter at google.com>
Date:   Thu Feb 8 19:39:57 2018 -0800

    Create the groups for a cmap format12. Does not yet build the actual table.

diff --git a/src/hb-ot-cmap-table.hh b/src/hb-ot-cmap-table.hh
index 9d4f0eec..bb3eba47 100644
--- a/src/hb-ot-cmap-table.hh
+++ b/src/hb-ot-cmap-table.hh
@@ -193,6 +193,7 @@ struct CmapSubtableLongGroup
 {
   friend struct CmapSubtableFormat12;
   friend struct CmapSubtableFormat13;
+  friend struct cmap;
 
   int cmp (hb_codepoint_t codepoint) const
   {
@@ -506,6 +507,30 @@ struct cmap
 
   inline bool subset (hb_subset_plan_t *plan, hb_face_t *source, hb_face_t *dest) const
   {
+    hb_auto_array_t<CmapSubtableLongGroup> groups;
+    CmapSubtableLongGroup *group = nullptr;
+    for (unsigned int i = 0; i < plan->codepoints.len; i++) {
+      hb_codepoint_t cp = plan->codepoints[i];
+      if (!group)
+      {
+        group = groups.push();
+        group->startCharCode.set(cp);
+        group->endCharCode.set(cp);
+        group->glyphID.set(i);  // index in codepoints is new gid
+      } else if (cp -1 == group->endCharCode)
+      {
+        group->endCharCode.set(cp);
+      } else
+      {
+        group = nullptr;
+      }
+    }
+
+    DEBUG_MSG(SUBSET, nullptr, "cmap");
+    for (unsigned int i = 0; i < groups.len; i++) {
+      CmapSubtableLongGroup& group = groups[i];
+      DEBUG_MSG(SUBSET, nullptr, "  %d: U+%04X-U+%04X, first gid %d", i, (uint32_t) group.startCharCode, (uint32_t) group.endCharCode, (uint32_t) group.glyphID);
+    }
     return true;
   }
 
commit 59c658c8d53481990fe0efa66422353d0687474b
Author: Rod Sheeter <rsheeter at google.com>
Date:   Thu Feb 8 19:22:47 2018 -0800

    capture codepoints sorted so we can use them for cmap later. one day we will have a map

diff --git a/src/hb-ot-cmap-table.hh b/src/hb-ot-cmap-table.hh
index 53733680..9d4f0eec 100644
--- a/src/hb-ot-cmap-table.hh
+++ b/src/hb-ot-cmap-table.hh
@@ -506,20 +506,6 @@ struct cmap
 
   inline bool subset (hb_subset_plan_t *plan, hb_face_t *source, hb_face_t *dest) const
   {
-    // TODO something useful re: memory, write to dest
-    size_t dest_sz = 64536; // as much as anyone would ever need
-    void *dest_buf = malloc(dest_sz);
-    OT::hb_serialize_context_t context(dest_buf, dest_sz);
-
-    // Same version
-    OT::cmap new_cmap;
-    new_cmap.version = version;
-    new_cmap.encodingRecord.len.set(1); // one format 12 subtable
-
-    // TODO we need to actually build the format 12 subtable
-
-    // TODO: this fails
-    // out->extend_min(new_cmap);
     return true;
   }
 
diff --git a/src/hb-private.hh b/src/hb-private.hh
index 59d732af..a3d1250a 100644
--- a/src/hb-private.hh
+++ b/src/hb-private.hh
@@ -418,34 +418,44 @@ struct hb_prealloced_array_t
     return &array[len - 1];
   }
 
-  inline bool resize (unsigned int size)
+  // Alloc enouch for size if size < allocated. Don't adjust len.
+  inline bool alloc(unsigned int size)
   {
-    if (unlikely (size > allocated))
+    if (likely (size <= allocated)) 
     {
-      /* Need to reallocate */
-
-      unsigned int new_allocated = allocated;
-      while (size >= new_allocated)
-        new_allocated += (new_allocated >> 1) + 8;
-
-      Type *new_array = nullptr;
-
-      if (array == static_array) {
-	new_array = (Type *) calloc (new_allocated, sizeof (Type));
-	if (new_array)
-	  memcpy (new_array, array, len * sizeof (Type));
-      } else {
-	bool overflows = (new_allocated < allocated) || _hb_unsigned_int_mul_overflows (new_allocated, sizeof (Type));
-	if (likely (!overflows)) {
-	  new_array = (Type *) realloc (array, new_allocated * sizeof (Type));
-	}
+      return true;
+    }
+    /* Need to reallocate */
+
+    unsigned int new_allocated = allocated;
+    while (size >= new_allocated)
+      new_allocated += (new_allocated >> 1) + 8;
+
+    Type *new_array = nullptr;
+
+    if (array == static_array) {
+      new_array = (Type *) calloc (new_allocated, sizeof (Type));
+      if (new_array)
+        memcpy (new_array, array, len * sizeof (Type));
+          } else {
+      bool overflows = (new_allocated < allocated) || _hb_unsigned_int_mul_overflows (new_allocated, sizeof (Type));
+      if (likely (!overflows)) {
+        new_array = (Type *) realloc (array, new_allocated * sizeof (Type));
       }
+    }
+
+    if (unlikely (!new_array))
+      return false;
 
-      if (unlikely (!new_array))
-	return false;
+    array = new_array;
+    allocated = new_allocated;    
+  }
 
-      array = new_array;
-      allocated = new_allocated;
+  inline bool resize (unsigned int size)
+  {
+    if (!alloc(size)) 
+    {
+      return false;
     }
 
     len = size;
@@ -488,6 +498,11 @@ struct hb_prealloced_array_t
     return nullptr;
   }
 
+  inline void qsort (int (*cmp)(const void*, const void*))
+  {
+    ::qsort (array, len, sizeof (Type), cmp);
+  }
+
   inline void qsort (void)
   {
     ::qsort (array, len, sizeof (Type), Type::cmp);
diff --git a/src/hb-subset-glyf.cc b/src/hb-subset-glyf.cc
index 49c52a91..b7412fbb 100644
--- a/src/hb-subset-glyf.cc
+++ b/src/hb-subset-glyf.cc
@@ -31,14 +31,14 @@
 
 bool
 _calculate_glyf_and_loca_prime_size (const OT::glyf::accelerator_t &glyf,
-                                     hb_set_t *glyph_ids,
+                                     hb_auto_array_t<unsigned int> &glyph_ids,
                                      unsigned int *glyf_size /* OUT */,
                                      unsigned int *loca_size /* OUT */)
 {
   unsigned int total = 0;
   unsigned int count = 0;
-  hb_codepoint_t next_glyph = -1;
-  while (hb_set_next(glyph_ids, &next_glyph)) {
+  for (unsigned int i = 0; i < glyph_ids.len; i++) {
+    hb_codepoint_t next_glyph = glyph_ids[i];
     unsigned int start_offset, end_offset;
     if (unlikely (!glyf.get_offsets (next_glyph, &start_offset, &end_offset))) {
       *glyf_size = 0;
@@ -58,7 +58,7 @@ _calculate_glyf_and_loca_prime_size (const OT::glyf::accelerator_t &glyf,
 bool
 _write_glyf_and_loca_prime (const OT::glyf::accelerator_t &glyf,
                             const char                    *glyf_data,
-                            const hb_set_t                *glyph_ids,
+                            hb_auto_array_t<unsigned int> &glyph_ids,
                             int                            glyf_prime_size,
                             char                          *glyf_prime_data /* OUT */,
                             int                            loca_prime_size,
@@ -73,9 +73,9 @@ _write_glyf_and_loca_prime (const OT::glyf::accelerator_t &glyf,
   hb_codepoint_t new_glyph_id = 0;
 
   unsigned int end_offset;
-  while (hb_set_next(glyph_ids, &next_glyph)) {
+  for (unsigned int i = 0; i < glyph_ids.len; i++) {
     unsigned int start_offset;
-    if (unlikely (!glyf.get_offsets (next_glyph, &start_offset, &end_offset))) {
+    if (unlikely (!glyf.get_offsets (glyph_ids[i], &start_offset, &end_offset))) {
       return false;
     }
 
@@ -97,7 +97,7 @@ _write_glyf_and_loca_prime (const OT::glyf::accelerator_t &glyf,
 bool
 _hb_subset_glyf_and_loca (const OT::glyf::accelerator_t  &glyf,
                           const char                     *glyf_data,
-                          hb_set_t                       *glyphs_to_retain,
+                          hb_auto_array_t<unsigned int>  &glyphs_to_retain,
                           hb_blob_t                     **glyf_prime /* OUT */,
                           hb_blob_t                     **loca_prime /* OUT */)
 {
@@ -158,7 +158,7 @@ hb_subset_glyf_and_loca (hb_subset_plan_t *plan,
 
   OT::glyf::accelerator_t glyf;
   glyf.init(face);
-  bool result = _hb_subset_glyf_and_loca (glyf, glyf_data, plan->glyphs_to_retain, glyf_prime, loca_prime);
+  bool result = _hb_subset_glyf_and_loca (glyf, glyf_data, plan->gids_to_retain, glyf_prime, loca_prime);
   glyf.fini();
 
   // TODO(grieger): Subset loca
diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc
index 6f889b3c..9dbc5a09 100644
--- a/src/hb-subset-plan.cc
+++ b/src/hb-subset-plan.cc
@@ -29,47 +29,75 @@
 #include "hb-subset-plan.hh"
 #include "hb-ot-cmap-table.hh"
 
+int hb_codepoint_t_cmp(const void *l, const void *r) {
+  return *((hb_codepoint_t *) l) - *((hb_codepoint_t *) r);
+}
+
 hb_bool_t
 hb_subset_plan_new_gid_for_old_id(hb_subset_plan_t *plan,
                                   hb_codepoint_t old_gid,
                                   hb_codepoint_t *new_gid) {
-  // TODO(Q1) lookup in map from old:new gid
-  // TEMPORARY: just loop over ids to retain and count up
-  hb_codepoint_t current = -1;
-  hb_codepoint_t count = 0;
-  while (hb_set_next(plan->glyphs_to_retain, &current)) {
-    if (old_gid == current) {
-      *new_gid = count;
+
+  // the index in old_gids is the new gid; only up to codepoints.len are valid
+  for (unsigned int i = 0; i < plan->codepoints.len; i++) {
+    if (plan->gids_to_retain[i] == old_gid) {
+      *new_gid = i;
       return true;
     }
-    count++;
   }
   return false;
 }
 
-hb_set_t *
-glyph_ids_to_retain (hb_face_t *face,
-                     hb_set_t  *codepoints)
+void populate_codepoints(hb_set_t *input_codepoints,
+                         hb_auto_array_t<hb_codepoint_t>& plan_codepoints) {
+  plan_codepoints.alloc(hb_set_get_population(input_codepoints));
+  hb_codepoint_t cp = -1;
+  while (hb_set_next(input_codepoints, &cp)) {
+    hb_codepoint_t *wr = plan_codepoints.push();
+    *wr = cp;
+  }
+  plan_codepoints.qsort(hb_codepoint_t_cmp);
+}
+
+void
+populate_gids_to_retain (hb_face_t *face,
+                         hb_auto_array_t<hb_codepoint_t>& codepoints,
+                         hb_auto_array_t<hb_codepoint_t>& old_gids)
 {
   OT::cmap::accelerator_t cmap;
   cmap.init (face);
-  hb_codepoint_t cp = -1;
-  hb_set_t *gids = hb_set_create();
-  while (hb_set_next(codepoints, &cp)) {
+
+  hb_auto_array_t<unsigned int> bad_indices;
+
+  old_gids.alloc(codepoints.len);
+  for (unsigned int i = 0; i < codepoints.len; i++) {
     hb_codepoint_t gid;
-    if (cmap.get_nominal_glyph(cp, &gid)) {
-      DEBUG_MSG(SUBSET, nullptr, "gid for U+%04X is %d", cp, gid);
-      hb_set_add(gids, gid);
-    } else {
-      DEBUG_MSG(SUBSET, nullptr, "Unable to resolve gid for U+%04X", cp);
+    if (!cmap.get_nominal_glyph(codepoints[i], &gid)) {
+      gid = -1;
+      *(bad_indices.push()) = i;
     }
+    *(old_gids.push()) = gid;
+  }
+
+  while (bad_indices.len > 0) {
+    unsigned int i = bad_indices[bad_indices.len - 1];
+    bad_indices.pop();
+    DEBUG_MSG(SUBSET, nullptr, "Drop U+%04X; no gid", codepoints[i]);
+    codepoints.remove(i);
+    old_gids.remove(i);
   }
 
+  for (unsigned int i = 0; i < codepoints.len; i++) {
+      DEBUG_MSG(SUBSET, nullptr, " U+%04X, old_gid %d, new_gid %d", codepoints[i], old_gids[i], i);
+  }
+
+  // TODO always keep .notdef
+
+
   // TODO(Q1) expand with glyphs that make up complex glyphs
   // TODO expand with glyphs reached by G*
   //
   cmap.fini ();
-  return gids;
 }
 
 /**
@@ -88,7 +116,8 @@ hb_subset_plan_create (hb_face_t           *face,
                        hb_subset_input_t   *input)
 {
   hb_subset_plan_t *plan = hb_object_create<hb_subset_plan_t> ();
-  plan->glyphs_to_retain = glyph_ids_to_retain (face, input->codepoints);
+  populate_codepoints(input->codepoints, plan->codepoints);
+  populate_gids_to_retain(face, plan->codepoints, plan->gids_to_retain);
   return plan;
 }
 
@@ -96,7 +125,6 @@ hb_subset_plan_t *
 hb_subset_plan_get_empty ()
 {
   hb_subset_plan_t *plan = hb_object_create<hb_subset_plan_t> ();
-  plan->glyphs_to_retain = hb_set_get_empty();
   return plan;
 }
 
@@ -110,6 +138,7 @@ hb_subset_plan_destroy (hb_subset_plan_t *plan)
 {
   if (!hb_object_destroy (plan)) return;
 
-  hb_set_destroy (plan->glyphs_to_retain);
+  plan->codepoints.finish();
+  plan->gids_to_retain.finish();
   free (plan);
 }
diff --git a/src/hb-subset-plan.hh b/src/hb-subset-plan.hh
index a1e4e9e9..c7e9108c 100644
--- a/src/hb-subset-plan.hh
+++ b/src/hb-subset-plan.hh
@@ -21,7 +21,7 @@
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
- * Google Author(s): Garret Rieger
+ * Google Author(s): Garret Rieger, Roderick Sheeter
  */
 
 #ifndef HB_SUBSET_PLAN_HH
@@ -35,7 +35,11 @@ struct hb_subset_plan_t {
   hb_object_header_t header;
   ASSERT_POD ();
 
-  hb_set_t *glyphs_to_retain;
+  // TODO(Q1) actual map, drop this crap
+  // Look at me ma, I'm a poor mans map codepoint : new gid
+  // codepoints is sorted and aligned with gids_to_retain.
+  hb_auto_array_t<hb_codepoint_t> codepoints;
+  hb_auto_array_t<hb_codepoint_t> gids_to_retain;
 };
 
 typedef struct hb_subset_plan_t hb_subset_plan_t;
diff --git a/src/hb-subset.cc b/src/hb-subset.cc
index f7c215bc..50bcac79 100644
--- a/src/hb-subset.cc
+++ b/src/hb-subset.cc
@@ -122,8 +122,8 @@ subset (hb_subset_plan_t *plan, hb_face_t *source, hb_face_t *dest)
 
     hb_blob_destroy (table_blob);
 
-    // TODO string not numeric tag
-    DEBUG_MSG(SUBSET, nullptr, "Subset %d %s", TableType::tableTag, result ? "success" : "FAILED!");
+    hb_tag_t tag = TableType::tableTag;
+    DEBUG_MSG(SUBSET, nullptr, "Subset %c%c%c%c %s", HB_UNTAG(tag), result ? "success" : "FAILED!");
     return result;
 }
 
@@ -316,14 +316,23 @@ hb_subset (hb_face_t *source,
 
   hb_subset_plan_t *plan = hb_subset_plan_create (source, profile, input);
 
-  hb_codepoint_t old_gid = -1;
-  while (hb_set_next (plan->glyphs_to_retain, &old_gid)) {
-    hb_codepoint_t new_gid;
-    if (hb_subset_plan_new_gid_for_old_id (plan, old_gid, &new_gid)) {
-      DEBUG_MSG (SUBSET, nullptr, "Remap %d : %d", old_gid, new_gid);
-    } else {
-      DEBUG_MSG (SUBSET, nullptr, "Remap %d : DOOM! No new ID", old_gid);
-    }
+  hb_face_t *face = hb_subset_face_create ();
+
+  /* Copy tables to new face. */
+  {
+    hb_tag_t table_tags[32];
+    unsigned int offset = 0, count;
+    do {
+      count = ARRAY_LENGTH (table_tags);
+      hb_face_get_table_tags (source, offset, &count, table_tags);
+      for (unsigned int i = 0; i < count; i++)
+      {
+      	hb_tag_t tag = table_tags[i];
+      	hb_blob_t *blob = hb_face_reference_table (source, tag);
+      	hb_subset_face_add_table (face, tag, blob);
+      	hb_blob_destroy (blob);
+      }
+    } while (count == ARRAY_LENGTH (table_tags));
   }
 
   hb_face_t *dest = hb_subset_face_create ();
commit 8431c38cdc05ddcddb1aa5fbb72a95446b500fd2
Author: Rod Sheeter <rsheeter at google.com>
Date:   Thu Feb 8 19:20:58 2018 -0800

    remove output noise

diff --git a/util/hb-subset.cc b/util/hb-subset.cc
index 37ec7b51..db1ca115 100644
--- a/util/hb-subset.cc
+++ b/util/hb-subset.cc
@@ -59,7 +59,6 @@ struct subset_consumer_t
       gunichar cp = g_utf8_get_char(c);
       hb_codepoint_t hb_cp = cp; // TODO(Q1) is this safe?
       hb_set_add(codepoints, hb_cp);
-      g_print ("  U+%04X %" G_GINT32_FORMAT "\n", cp, cp);
     } while ((c = g_utf8_find_next_char(c, text + text_len)) != nullptr);
   }
 
commit 5cca0c07afbe9ab4b28d333f6f853063ecd75aff
Author: Rod Sheeter <rsheeter at google.com>
Date:   Thu Feb 8 19:05:46 2018 -0800

    fix comment

diff --git a/src/hb-open-file-private.hh b/src/hb-open-file-private.hh
index d0e03ddc..e425afc3 100644
--- a/src/hb-open-file-private.hh
+++ b/src/hb-open-file-private.hh
@@ -150,7 +150,7 @@ typedef struct OffsetTable
       rec.length.set (hb_blob_get_length (blob));
       rec.checkSum.set_for_data (hb_blob_get_data (blob, nullptr), rec.length);
       rec.offset.serialize (c, this);
-      // take room for the tablerec
+      // take room for the table
       void *p = c->allocate_size<void> (rec.length);
       if (unlikely (!p)) {return false;}
       // copy the actual table
commit a19138e668e77a0c05da2ab065c5366c8359b377
Author: Rod Sheeter <rsheeter at google.com>
Date:   Thu Feb 8 19:03:41 2018 -0800

    comment the serialization of table

diff --git a/src/hb-open-file-private.hh b/src/hb-open-file-private.hh
index ab168ab8..d0e03ddc 100644
--- a/src/hb-open-file-private.hh
+++ b/src/hb-open-file-private.hh
@@ -133,10 +133,15 @@ typedef struct OffsetTable
 			 unsigned int table_count)
   {
     TRACE_SERIALIZE (this);
+    // alloc 12 for the OTHeader
     if (unlikely (!c->extend_min (*this))) return_trace (false);
+    // write sfntVersion (bytes 0..3)
     sfnt_version.set (sfnt_tag);
+    // take space for numTables, searchRange, entrySelector, RangeShift
+    // and the TableRecords themselves
     if (unlikely (!tables.serialize (c, table_count))) return_trace (false);
 
+    // write OffsetTables, alloc for and write actual table blobs
     for (unsigned int i = 0; i < table_count; i++)
     {
       TableRecord &rec = tables.array[i];
@@ -145,9 +150,12 @@ typedef struct OffsetTable
       rec.length.set (hb_blob_get_length (blob));
       rec.checkSum.set_for_data (hb_blob_get_data (blob, nullptr), rec.length);
       rec.offset.serialize (c, this);
+      // take room for the tablerec
       void *p = c->allocate_size<void> (rec.length);
       if (unlikely (!p)) {return false;}
+      // copy the actual table
       memcpy (p, hb_blob_get_data (blob, nullptr), rec.length);
+      // 4-byte allignment
       if (rec.length % 4)
 	p = c->allocate_size<void> (4 - rec.length % 4);
     }
commit 5a34114f9685680d4a8cdf85a8ac90172c5620d7
Author: Garret Rieger <grieger at google.com>
Date:   Thu Feb 8 18:32:24 2018 -0800

    Add an extra entry to the end of the loca table to identify the end of the last glyph's data.

diff --git a/src/hb-subset-glyf.cc b/src/hb-subset-glyf.cc
index d7edd750..49c52a91 100644
--- a/src/hb-subset-glyf.cc
+++ b/src/hb-subset-glyf.cc
@@ -72,8 +72,9 @@ _write_glyf_and_loca_prime (const OT::glyf::accelerator_t &glyf,
   hb_codepoint_t next_glyph = -1;
   hb_codepoint_t new_glyph_id = 0;
 
+  unsigned int end_offset;
   while (hb_set_next(glyph_ids, &next_glyph)) {
-    unsigned int start_offset, end_offset;
+    unsigned int start_offset;
     if (unlikely (!glyf.get_offsets (next_glyph, &start_offset, &end_offset))) {
       return false;
     }
@@ -86,6 +87,10 @@ _write_glyf_and_loca_prime (const OT::glyf::accelerator_t &glyf,
     new_glyph_id++;
   }
 
+  // Add the last loca entry which doesn't correspond to a specific glyph
+  // but identifies the end of the last glyphs data.
+  loca_prime[new_glyph_id].set(end_offset);
+
   return true;
 }
 
commit aac7d962120aa137385324b33a173df4f19fd80b
Author: Garret Rieger <grieger at google.com>
Date:   Thu Feb 8 18:18:16 2018 -0800

    Apply per table subsetting while building the new face in hb_subset.

diff --git a/src/hb-subset-glyf.cc b/src/hb-subset-glyf.cc
index 8221a43d..d7edd750 100644
--- a/src/hb-subset-glyf.cc
+++ b/src/hb-subset-glyf.cc
@@ -101,6 +101,7 @@ _hb_subset_glyf_and_loca (const OT::glyf::accelerator_t  &glyf,
   // TODO(grieger): Subset loca simultaneously.
   // TODO(grieger): Don't fail on bad offsets, just dump them.
   // TODO(grieger): Support short loca output.
+  // TODO(grieger): Add a extra loca entry at the end.
 
   unsigned int glyf_prime_size;
   unsigned int loca_prime_size;
diff --git a/src/hb-subset.cc b/src/hb-subset.cc
index a46cbd08..f7c215bc 100644
--- a/src/hb-subset.cc
+++ b/src/hb-subset.cc
@@ -253,6 +253,52 @@ hb_subset_face_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob)
   return true;
 }
 
+bool
+_subset_glyf (hb_subset_plan_t *plan, hb_face_t *source, hb_face_t *dest)
+{
+  hb_blob_t *glyf_prime = nullptr;
+  hb_blob_t *loca_prime = nullptr;
+
+  bool success = true;
+  // TODO(grieger): Migrate to subset function on the table like cmap.
+  if (hb_subset_glyf_and_loca (plan, source, &glyf_prime, &loca_prime)) {
+    hb_subset_face_add_table (dest, HB_OT_TAG_glyf, glyf_prime);
+    hb_subset_face_add_table (dest, HB_OT_TAG_loca, loca_prime);
+  } else {
+    success = false;
+  }
+  hb_blob_destroy (loca_prime);
+  hb_blob_destroy (glyf_prime);
+
+  return success;
+}
+
+bool
+_subset_table (hb_subset_plan_t *plan,
+               hb_face_t        *source,
+               hb_tag_t          tag,
+               hb_blob_t        *table_blob,
+               hb_face_t        *dest)
+{
+  // TODO (grieger): Handle updating the head table (loca format + num glyphs)
+  switch (tag) {
+    case HB_OT_TAG_glyf:
+      return _subset_glyf (plan, source, dest);
+    case HB_OT_TAG_loca:
+      // SKIP loca, it's handle by the glyf subsetter.
+      return true;
+    case HB_OT_TAG_cmap:
+      // TODO(rsheeter): remove hb_subset_face_add_table
+      //                 once cmap subsetting works.
+      hb_subset_face_add_table (dest, tag, table_blob);
+      return subset<const OT::cmap> (plan, source, dest);
+    default:
+      // Default action, copy table as is.
+      hb_subset_face_add_table (dest, tag, table_blob);
+      return true;
+  }
+}
+
 /**
  * hb_subset:
  * @source: font face data to be subset.
@@ -270,25 +316,6 @@ hb_subset (hb_face_t *source,
 
   hb_subset_plan_t *plan = hb_subset_plan_create (source, profile, input);
 
-  hb_face_t *face = hb_subset_face_create ();
-
-  /* Copy tables to new face. */
-  {
-    hb_tag_t table_tags[32];
-    unsigned int offset = 0, count;
-    do {
-      count = ARRAY_LENGTH (table_tags);
-      hb_face_get_table_tags (source, offset, &count, table_tags);
-      for (unsigned int i = 0; i < count; i++)
-      {
-	hb_tag_t tag = table_tags[i];
-	hb_blob_t *blob = hb_face_reference_table (source, tag);
-	hb_subset_face_add_table (face, tag, blob);
-	hb_blob_destroy (blob);
-      }
-    } while (count == ARRAY_LENGTH (table_tags));
-  }
-
   hb_codepoint_t old_gid = -1;
   while (hb_set_next (plan->glyphs_to_retain, &old_gid)) {
     hb_codepoint_t new_gid;
@@ -298,29 +325,25 @@ hb_subset (hb_face_t *source,
       DEBUG_MSG (SUBSET, nullptr, "Remap %d : DOOM! No new ID", old_gid);
     }
   }
-  // TODO:
-  // - Create initial header + table directory
-  // - Loop through the set of tables to be kept:
-  //   - Perform table specific subsetting if defined.
-  //   - copy the table into the output.
-  // - Fix header + table directory.
 
+  hb_face_t *dest = hb_subset_face_create ();
+  hb_tag_t table_tags[32];
+  unsigned int offset = 0, count;
   bool success = true;
+  do {
+    count = ARRAY_LENGTH (table_tags);
+    hb_face_get_table_tags (source, offset, &count, table_tags);
+    for (unsigned int i = 0; i < count; i++)
+    {
+      hb_tag_t tag = table_tags[i];
+      hb_blob_t *blob = hb_face_reference_table (source, tag);
+      success = success && _subset_table (plan, source, tag, blob, dest);
+      hb_blob_destroy (blob);
+    }
+  } while (count == ARRAY_LENGTH (table_tags));
 
-  hb_face_t *dest = nullptr; // TODO allocate dest
-
-  hb_blob_t *glyf_prime = nullptr;
-  hb_blob_t *loca_prime = nullptr;
-  if (hb_subset_glyf_and_loca (plan, source, &glyf_prime, &loca_prime)) {
-    // TODO: write new glyf and loca to new face.
-  } else {
-    success = false;
-  }
-  hb_blob_destroy (glyf_prime);
-
-  success = success && subset<const OT::cmap>(plan, source, dest);
-
-  hb_subset_plan_destroy (plan);
-
-  return face;
+  // TODO(grieger): Remove once basic subsetting is working + tests updated.
+  hb_face_destroy (dest);
+  hb_face_reference (source);
+  return success ? source : nullptr;
 }
commit 2f941053111d60433ab39cc70edd69c962896961
Author: Garret Rieger <grieger at google.com>
Date:   Thu Feb 8 15:55:12 2018 -0800

    Disable subset tests on cmake for now.

diff --git a/test/subset/CMakeLists.txt b/test/subset/CMakeLists.txt
index 0a1e8f95..6fe377e1 100644
--- a/test/subset/CMakeLists.txt
+++ b/test/subset/CMakeLists.txt
@@ -2,8 +2,9 @@ if (HB_BUILD_UTILS)
   file (READ "${CMAKE_CURRENT_SOURCE_DIR}/data/Makefile.sources" SOURCES)
   extract_make_variable (TESTS ${SOURCES})
   foreach (test IN ITEMS ${TESTS})
-    add_test (NAME ${test}
-      COMMAND python run-tests.py $<TARGET_FILE:hb-subset> "data/${test}"
-      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+# TODO(grieger): Re-enable once ttx is available in CI environments.
+#    add_test (NAME ${test}
+#      COMMAND python run-tests.py $<TARGET_FILE:hb-subset> "data/${test}"
+#      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
   endforeach ()
 endif ()


More information about the HarfBuzz mailing list