Merge pull request #3437 from matthiasclasen/synthesize-missing-baselines

[BASE] Synthesize missing baselines
diff --git a/BUILD.md b/BUILD.md
index f64f868..47681ec 100644
--- a/BUILD.md
+++ b/BUILD.md
@@ -18,7 +18,8 @@
 
 On Windows, meson can build the project like above if a working MSVC's cl.exe (`vcvarsall.bat`)
 or gcc/clang is already on your path, and if you use something like `meson build --wrap-mode=default`
-it fetches and compiles most of the dependencies also.
+it fetches and compiles most of the dependencies also.  It is recommended to install CMake either
+manually or via the Visual Studio installer when building with MSVC when building with meson.
 
 Our CI configurations is also a good source of learning how to build HarfBuzz.
 
diff --git a/docs/harfbuzz-sections.txt b/docs/harfbuzz-sections.txt
index f81058b..0f44e9a 100644
--- a/docs/harfbuzz-sections.txt
+++ b/docs/harfbuzz-sections.txt
@@ -565,12 +565,6 @@
 hb_ot_layout_table_select_script
 hb_ot_shape_plan_collect_lookups
 hb_ot_layout_language_get_required_feature_index
-<SUBSECTION Private>
-Xhb_ot_layout_lookup_enumerate_sequences
-Xhb_ot_layout_lookup_position
-Xhb_ot_layout_lookup_substitute
-hb_ot_layout_glyph_sequence_t
-hb_ot_layout_glyph_sequence_func_t
 </SECTION>
 
 <SECTION>
diff --git a/meson.build b/meson.build
index 8a9400c..e22859a 100644
--- a/meson.build
+++ b/meson.build
@@ -83,50 +83,25 @@
 
 m_dep = cpp.find_library('m', required: false)
 
-freetype_dep = null_dep
-if not get_option('freetype').disabled()
-  freetype_dep = dependency('freetype2', required: false)
-
-  if (not freetype_dep.found() and
-      cpp.get_id() == 'msvc' and
-      cpp.has_header('ft2build.h'))
-    freetype_dep = cpp.find_library('freetype', required: false)
-  endif
-
-  if not freetype_dep.found()
-    # https://github.com/harfbuzz/harfbuzz/pull/2498
-    freetype_dep = dependency('freetype2', required: get_option('freetype'),
-                              default_options: ['harfbuzz=disabled'])
-  endif
-endif
+# https://github.com/harfbuzz/harfbuzz/pull/2498
+freetype_dep = dependency(cpp.get_argument_syntax() == 'msvc' ? 'freetype' : 'freetype2',
+                          required: get_option('freetype'),
+                          default_options: ['harfbuzz=disabled'])
 
 glib_dep = dependency('glib-2.0', required: get_option('glib'))
 gobject_dep = dependency('gobject-2.0', required: get_option('gobject'))
 graphite2_dep = dependency('graphite2', required: get_option('graphite2'))
 graphite_dep = dependency('graphite2', required: get_option('graphite'))
 
-icu_dep = null_dep
-if not get_option('icu').disabled()
-  icu_dep = dependency('icu-uc', required: false)
-
-  if (not icu_dep.found() and
-      cpp.get_id() == 'msvc' and
-      cpp.has_header('unicode/uchar.h') and
-      cpp.has_header('unicode/unorm2.h') and
-      cpp.has_header('unicode/ustring.h') and
-      cpp.has_header('unicode/utf16.h') and
-      cpp.has_header('unicode/uversion.h') and
-      cpp.has_header('unicode/uscript.h'))
-    if get_option('buildtype') == 'debug'
-      icu_dep = cpp.find_library('icuucd', required: false)
-    else
-      icu_dep = cpp.find_library('icuuc', required: false)
-    endif
-  endif
-
-  if not icu_dep.found()
-    icu_dep = dependency('icu-uc', required: get_option('icu'))
-  endif
+if cpp.get_argument_syntax() == 'msvc'
+  icu_dep = dependency('ICU',
+                       required: get_option('icu'),
+                       components: 'uc',
+                       method: 'cmake')
+else
+  icu_dep = dependency('icu-uc',
+                       required: get_option('icu'),
+                       method: 'pkg-config')
 endif
 
 if icu_dep.found() and icu_dep.type_name() == 'pkgconfig'
diff --git a/src/hb-algs.hh b/src/hb-algs.hh
index 23bb42e..c40a55c 100644
--- a/src/hb-algs.hh
+++ b/src/hb-algs.hh
@@ -226,14 +226,8 @@
   template <typename T> constexpr auto
   impl (const T& v, hb_priority<2>) const HB_RETURN (uint32_t, hb_deref (v).hash ())
 
-/* Sadly, we must give further hints to VS2015 to build the following template item */
-#if !defined (_MSC_VER) || defined (__clang__) || (_MSC_VER >= 1910)
   template <typename T> constexpr auto
   impl (const T& v, hb_priority<1>) const HB_RETURN (uint32_t, std::hash<hb_decay<decltype (hb_deref (v))>>{} (hb_deref (v)))
-#else
-  template <typename T> constexpr auto
-  impl (const T& v, hb_priority<1>) const HB_RETURN (uint32_t, std::hash<hb_decay<decltype (hb_deref (v).hash ())>>{} (hb_deref (v)))
-#endif
 
   template <typename T,
 	    hb_enable_if (std::is_integral<T>::value)> constexpr auto
diff --git a/src/hb-config.hh b/src/hb-config.hh
index 5141ad8..4b46dea 100644
--- a/src/hb-config.hh
+++ b/src/hb-config.hh
@@ -85,6 +85,7 @@
 #ifdef HB_MINI
 #define HB_NO_AAT
 #define HB_NO_LEGACY
+#define HB_NO_BORING_EXPANSION
 #endif
 
 #if defined(HAVE_CONFIG_OVERRIDE_H) || defined(HB_CONFIG_OVERRIDE_H)
diff --git a/src/hb-machinery.hh b/src/hb-machinery.hh
index b529173..e52a6a4 100644
--- a/src/hb-machinery.hh
+++ b/src/hb-machinery.hh
@@ -273,14 +273,19 @@
 						hb_face_lazy_loader_t<T, WheresFace>,
 						hb_face_t, WheresFace> {};
 
-template <typename T, unsigned int WheresFace>
+template <typename T, unsigned int WheresFace, bool core=false>
 struct hb_table_lazy_loader_t : hb_lazy_loader_t<T,
-						 hb_table_lazy_loader_t<T, WheresFace>,
+						 hb_table_lazy_loader_t<T, WheresFace, core>,
 						 hb_face_t, WheresFace,
 						 hb_blob_t>
 {
   static hb_blob_t *create (hb_face_t *face)
-  { return hb_sanitize_context_t ().reference_table<T> (face); }
+  {
+    auto c = hb_sanitize_context_t ();
+    if (core)
+      c.set_num_glyphs (0); // So we don't recurse ad infinitum...
+    return c.reference_table<T> (face);
+  }
   static void destroy (hb_blob_t *p) { hb_blob_destroy (p); }
 
   static const hb_blob_t *get_null ()
diff --git a/src/hb-ot-face-table-list.hh b/src/hb-ot-face-table-list.hh
index eff0983..c05034b 100644
--- a/src/hb-ot-face-table-list.hh
+++ b/src/hb-ot-face-table-list.hh
@@ -32,6 +32,11 @@
 #define HB_OT_FACE_TABLE_LIST_HH
 #endif /* HB_OT_FACE_TABLE_LIST_HH */ /* Dummy header guards */
 
+#ifndef HB_OT_CORE_TABLE
+#define HB_OT_CORE_TABLE(Namespace, Type) HB_OT_TABLE (Namespace, Type)
+#define _HB_OT_CORE_TABLE_UNDEF
+#endif
+
 #ifndef HB_OT_ACCELERATOR
 #define HB_OT_ACCELERATOR(Namespace, Type) HB_OT_TABLE (Namespace, Type)
 #define _HB_OT_ACCELERATOR_UNDEF
@@ -46,7 +51,8 @@
 
 
 /* OpenType fundamentals. */
-HB_OT_TABLE (OT, head)
+HB_OT_CORE_TABLE (OT, head)
+HB_OT_CORE_TABLE (OT, maxp)
 #if !defined(HB_NO_FACE_COLLECT_UNICODES) || !defined(HB_NO_OT_FONT)
 HB_OT_ACCELERATOR (OT, cmap)
 #endif
@@ -74,6 +80,7 @@
 #endif
 
 /* TrueType outlines. */
+HB_OT_CORE_TABLE (OT, loca) // Also used to determine number of glyphs
 HB_OT_ACCELERATOR (OT, glyf)
 
 /* CFF outlines. */
@@ -138,3 +145,7 @@
 #ifdef _HB_OT_ACCELERATOR_UNDEF
 #undef HB_OT_ACCELERATOR
 #endif
+
+#ifdef _HB_OT_CORE_TABLE_UNDEF
+#undef HB_OT_CORE_TABLE
+#endif
diff --git a/src/hb-ot-face.hh b/src/hb-ot-face.hh
index e24d380..415dae8 100644
--- a/src/hb-ot-face.hh
+++ b/src/hb-ot-face.hh
@@ -63,10 +63,13 @@
   hb_face_t *face; /* MUST be JUST before the lazy loaders. */
 #define HB_OT_TABLE(Namespace, Type) \
   hb_table_lazy_loader_t<Namespace::Type, HB_OT_TABLE_ORDER (Namespace, Type)> Type;
+#define HB_OT_CORE_TABLE(Namespace, Type) \
+  hb_table_lazy_loader_t<Namespace::Type, HB_OT_TABLE_ORDER (Namespace, Type), true> Type;
 #define HB_OT_ACCELERATOR(Namespace, Type) \
   hb_face_lazy_loader_t<Namespace::Type##_accelerator_t, HB_OT_TABLE_ORDER (Namespace, Type)> Type;
 #include "hb-ot-face-table-list.hh"
 #undef HB_OT_ACCELERATOR
+#undef HB_OT_CORE_TABLE
 #undef HB_OT_TABLE
 };
 
diff --git a/src/hb-ot-glyf-table.hh b/src/hb-ot-glyf-table.hh
index 2c26a8d..066e152 100644
--- a/src/hb-ot-glyf-table.hh
+++ b/src/hb-ot-glyf-table.hh
@@ -936,7 +936,7 @@
 	return;
       short_offset = 0 == head.indexToLocFormat;
 
-      loca_table = hb_sanitize_context_t ().reference_table<loca> (face);
+      loca_table = face->table.loca.get_blob (); // Needs no destruct!
       glyf_table = hb_sanitize_context_t ().reference_table<glyf> (face);
 #ifndef HB_NO_VAR
       gvar = face->table.gvar;
@@ -951,7 +951,6 @@
     }
     ~accelerator_t ()
     {
-      loca_table.destroy ();
       glyf_table.destroy ();
     }
 
diff --git a/src/hb-ot-hmtx-table.hh b/src/hb-ot-hmtx-table.hh
index 739474f..7487e40 100644
--- a/src/hb-ot-hmtx-table.hh
+++ b/src/hb-ot-hmtx-table.hh
@@ -28,6 +28,7 @@
 #define HB_OT_HMTX_TABLE_HH
 
 #include "hb-open-type.hh"
+#include "hb-ot-maxp-table.hh"
 #include "hb-ot-hhea-table.hh"
 #include "hb-ot-var-hvar-table.hh"
 #include "hb-ot-metrics.hh"
@@ -98,12 +99,12 @@
 	   hb_requires (hb_is_iterator (Iterator))>
   void serialize (hb_serialize_context_t *c,
 		  Iterator it,
-		  unsigned num_advances)
+		  unsigned num_long_metrics)
   {
     unsigned idx = 0;
     for (auto _ : it)
     {
-      if (idx < num_advances)
+      if (idx < num_long_metrics)
       {
 	LongMetric lm;
 	lm.advance = _.first;
@@ -128,17 +129,17 @@
     if (unlikely (!table_prime)) return_trace (false);
 
     accelerator_t _mtx (c->plan->source);
-    unsigned num_advances;
+    unsigned num_long_metrics;
     {
-      /* Determine num_advances to encode. */
+      /* Determine num_long_metrics to encode. */
       auto& plan = c->plan;
-      num_advances = plan->num_output_glyphs ();
+      num_long_metrics = plan->num_output_glyphs ();
       hb_codepoint_t old_gid = 0;
-      unsigned int last_advance = plan->old_gid_for_new_gid (num_advances - 1, &old_gid) ? _mtx.get_advance (old_gid) : 0;
-      while (num_advances > 1 &&
-	     last_advance == (plan->old_gid_for_new_gid (num_advances - 2, &old_gid) ? _mtx.get_advance (old_gid) : 0))
+      unsigned int last_advance = plan->old_gid_for_new_gid (num_long_metrics - 1, &old_gid) ? _mtx.get_advance (old_gid) : 0;
+      while (num_long_metrics > 1 &&
+	     last_advance == (plan->old_gid_for_new_gid (num_long_metrics - 2, &old_gid) ? _mtx.get_advance (old_gid) : 0))
       {
-	num_advances--;
+	num_long_metrics--;
       }
     }
 
@@ -153,13 +154,13 @@
 	      })
     ;
 
-    table_prime->serialize (c->serializer, it, num_advances);
+    table_prime->serialize (c->serializer, it, num_long_metrics);
 
     if (unlikely (c->serializer->in_error ()))
       return_trace (false);
 
     // Amend header num hmetrics
-    if (unlikely (!subset_update_header (c->plan, num_advances)))
+    if (unlikely (!subset_update_header (c->plan, num_long_metrics)))
       return_trace (false);
 
     return_trace (true);
@@ -172,35 +173,46 @@
     accelerator_t (hb_face_t *face,
 		   unsigned int default_advance_ = 0)
     {
+      table = hb_sanitize_context_t ().reference_table<hmtxvmtx> (face, T::tableTag);
+      var_table = hb_sanitize_context_t ().reference_table<HVARVVAR> (face, T::variationsTag);
+
       default_advance = default_advance_ ? default_advance_ : hb_face_get_upem (face);
 
-      num_advances = T::is_horizontal ?
-		     face->table.hhea->numberOfLongMetrics :
-#ifndef HB_NO_VERTICAL
-		     face->table.vhea->numberOfLongMetrics
-#else
-		     0
-#endif
-		     ;
+      /* Populate count variables and sort them out as we go */
 
-      table = hb_sanitize_context_t ().reference_table<hmtxvmtx> (face, T::tableTag);
-
-      /* Cap num_metrics() and num_advances() based on table length. */
       unsigned int len = table.get_length ();
-      if (unlikely (num_advances * 4 > len))
-	num_advances = len / 4;
-      num_metrics = num_advances + (len - 4 * num_advances) / 2;
+      if (len & 1)
+        len--;
 
-      /* We MUST set num_metrics to zero if num_advances is zero.
+      num_long_metrics = T::is_horizontal ?
+			 face->table.hhea->numberOfLongMetrics :
+#ifndef HB_NO_VERTICAL
+			 face->table.vhea->numberOfLongMetrics
+#else
+			 0
+#endif
+			 ;
+      if (unlikely (num_long_metrics * 4 > len))
+	num_long_metrics = len / 4;
+      len -= num_long_metrics * 4;
+
+      num_bearings = face->table.maxp->get_num_glyphs ();
+
+      if (unlikely (num_bearings < num_long_metrics))
+        num_bearings = num_long_metrics;
+      if (unlikely ((num_bearings - num_long_metrics) * 2 > len))
+        num_bearings = num_long_metrics + len / 2;
+      len -= (num_bearings - num_long_metrics) * 2;
+
+      /* We MUST set num_bearings to zero if num_long_metrics is zero.
        * Our get_advance() depends on that. */
-      if (unlikely (!num_advances))
-      {
-	num_metrics = num_advances = 0;
-	table.destroy ();
-	table = hb_blob_get_empty ();
-      }
+      if (unlikely (!num_long_metrics))
+	num_bearings = num_long_metrics = 0;
 
-      var_table = hb_sanitize_context_t ().reference_table<HVARVVAR> (face, T::variationsTag);
+      num_advances = num_bearings + len / 2;
+      num_glyphs = face->get_num_glyphs ();
+      if (num_glyphs < num_advances)
+        num_glyphs = num_advances;
     }
     ~accelerator_t ()
     {
@@ -210,14 +222,14 @@
 
     int get_side_bearing (hb_codepoint_t glyph) const
     {
-      if (glyph < num_advances)
+      if (glyph < num_long_metrics)
 	return table->longMetricZ[glyph].sb;
 
-      if (unlikely (glyph >= num_metrics))
+      if (unlikely (glyph >= num_bearings))
 	return 0;
 
-      const FWORD *bearings = (const FWORD *) &table->longMetricZ[num_advances];
-      return bearings[glyph - num_advances];
+      const FWORD *bearings = (const FWORD *) &table->longMetricZ[num_long_metrics];
+      return bearings[glyph - num_long_metrics];
     }
 
     int get_side_bearing (hb_font_t *font, hb_codepoint_t glyph) const
@@ -225,7 +237,7 @@
       int side_bearing = get_side_bearing (glyph);
 
 #ifndef HB_NO_VAR
-      if (unlikely (glyph >= num_metrics) || !font->num_coords)
+      if (unlikely (glyph >= num_bearings) || !font->num_coords)
 	return side_bearing;
 
       if (var_table.get_length ())
@@ -239,18 +251,35 @@
 
     unsigned int get_advance (hb_codepoint_t glyph) const
     {
-      if (unlikely (glyph >= num_metrics))
-      {
-	/* If num_metrics is zero, it means we don't have the metrics table
-	 * for this direction: return default advance.  Otherwise, it means that the
-	 * glyph index is out of bound: return zero. */
-	if (num_metrics)
-	  return 0;
-	else
-	  return default_advance;
-      }
+      /* OpenType case. */
+      if (glyph < num_bearings)
+	return table->longMetricZ[hb_min (glyph, (uint32_t) num_long_metrics - 1)].advance;
 
-      return table->longMetricZ[hb_min (glyph, (uint32_t) num_advances - 1)].advance;
+      /* If num_advances is zero, it means we don't have the metrics table
+       * for this direction: return default advance.  Otherwise, there's a
+       * well-defined answer. */
+      if (unlikely (!num_advances))
+	return default_advance;
+
+#ifdef HB_NO_BORING_EXPANSION
+      return 0;
+#endif
+
+      if (unlikely (glyph >= num_glyphs))
+        return 0;
+
+      /* num_bearings <= glyph < num_glyphs;
+       * num_bearings <= num_advances */
+
+      /* TODO Optimize */
+
+      if (num_bearings == num_advances)
+        return get_advance (num_bearings - 1);
+
+      const FWORD *bearings = (const FWORD *) &table->longMetricZ[num_long_metrics];
+      const UFWORD *advances = (const UFWORD *) &bearings[num_bearings - num_long_metrics];
+
+      return advances[hb_min (glyph - num_bearings, num_advances - num_bearings - 1)];
     }
 
     unsigned int get_advance (hb_codepoint_t  glyph,
@@ -259,7 +288,7 @@
       unsigned int advance = get_advance (glyph);
 
 #ifndef HB_NO_VAR
-      if (unlikely (glyph >= num_metrics) || !font->num_coords)
+      if (unlikely (glyph >= num_bearings) || !font->num_coords)
 	return advance;
 
       if (var_table.get_length ())
@@ -272,8 +301,12 @@
     }
 
     protected:
-    unsigned int num_metrics;
-    unsigned int num_advances;
+    // 0 <= num_long_metrics <= num_bearings <= num_advances <= num_glyphs
+    unsigned num_long_metrics;
+    unsigned num_bearings;
+    unsigned num_advances;
+    unsigned num_glyphs;
+
     unsigned int default_advance;
 
     private:
@@ -305,6 +338,8 @@
 				 * the end. This allows a monospaced
 				 * font to vary the side bearing
 				 * values for each glyph. */
+/*UnsizedArrayOf<UFWORD>advancesX;*/
+				/* TODO Document. */
   public:
   DEFINE_SIZE_ARRAY (0, longMetricZ);
 };
diff --git a/src/hb-ot-layout.h b/src/hb-ot-layout.h
index ca94b59..6bc44e7 100644
--- a/src/hb-ot-layout.h
+++ b/src/hb-ot-layout.h
@@ -332,31 +332,6 @@
 				    hb_set_t     *glyphs_after,  /* OUT.  May be NULL */
 				    hb_set_t     *glyphs_output  /* OUT.  May be NULL */);
 
-#ifdef HB_NOT_IMPLEMENTED
-typedef struct
-{
-  const hb_codepoint_t *before,
-  unsigned int          before_length,
-  const hb_codepoint_t *input,
-  unsigned int          input_length,
-  const hb_codepoint_t *after,
-  unsigned int          after_length,
-} hb_ot_layout_glyph_sequence_t;
-
-typedef hb_bool_t
-(*hb_ot_layout_glyph_sequence_func_t) (hb_font_t    *font,
-				       hb_tag_t      table_tag,
-				       unsigned int  lookup_index,
-				       const hb_ot_layout_glyph_sequence_t *sequence,
-				       void         *user_data);
-
-HB_EXTERN void
-Xhb_ot_layout_lookup_enumerate_sequences (hb_face_t    *face,
-					 hb_tag_t      table_tag,
-					 unsigned int  lookup_index,
-					 hb_ot_layout_glyph_sequence_func_t callback,
-					 void         *user_data);
-#endif
 
 /* Variations support */
 
@@ -411,19 +386,6 @@
 					 hb_set_t       *glyphs);
 
 
-#ifdef HB_NOT_IMPLEMENTED
-/* Note: You better have GDEF when using this API, or marks won't do much. */
-HB_EXTERN hb_bool_t
-Xhb_ot_layout_lookup_substitute (hb_font_t            *font,
-				unsigned int          lookup_index,
-				const hb_ot_layout_glyph_sequence_t *sequence,
-				unsigned int          out_size,
-				hb_codepoint_t       *glyphs_out,   /* OUT */
-				unsigned int         *clusters_out, /* OUT */
-				unsigned int         *out_length    /* OUT */);
-#endif
-
-
 /*
  * GPOS
  */
@@ -431,15 +393,6 @@
 HB_EXTERN hb_bool_t
 hb_ot_layout_has_positioning (hb_face_t *face);
 
-#ifdef HB_NOT_IMPLEMENTED
-/* Note: You better have GDEF when using this API, or marks won't do much. */
-HB_EXTERN hb_bool_t
-Xhb_ot_layout_lookup_position (hb_font_t            *font,
-			      unsigned int          lookup_index,
-			      const hb_ot_layout_glyph_sequence_t *sequence,
-			      hb_glyph_position_t  *positions /* IN / OUT */);
-#endif
-
 /* Optical 'size' feature info.  Returns true if found.
  * https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#size */
 HB_EXTERN hb_bool_t
diff --git a/src/hb-static.cc b/src/hb-static.cc
index ec4b241..bd69881 100644
--- a/src/hb-static.cc
+++ b/src/hb-static.cc
@@ -33,6 +33,7 @@
 #include "hb-aat-layout-feat-table.hh"
 #include "hb-ot-layout-common.hh"
 #include "hb-ot-cmap-table.hh"
+#include "hb-ot-glyf-table.hh"
 #include "hb-ot-head-table.hh"
 #include "hb-ot-maxp-table.hh"
 
@@ -55,17 +56,41 @@
 
 /* hb_face_t */
 
+static inline unsigned
+load_num_glyphs_from_loca (const hb_face_t *face)
+{
+  unsigned ret = 0;
+
+  unsigned indexToLocFormat = face->table.head->indexToLocFormat;
+
+  if (indexToLocFormat <= 1)
+  {
+    bool short_offset = 0 == indexToLocFormat;
+    hb_blob_t *loca_blob = face->table.loca.get_blob ();
+    ret = hb_max (1u, loca_blob->length / (short_offset ? 2 : 4)) - 1;
+  }
+
+  return ret;
+}
+
+static inline unsigned
+load_num_glyphs_from_maxp (const hb_face_t *face)
+{
+  return face->table.maxp->get_num_glyphs ();
+}
+
 unsigned int
 hb_face_t::load_num_glyphs () const
 {
-  hb_sanitize_context_t c = hb_sanitize_context_t ();
-  c.set_num_glyphs (0); /* So we don't recurse ad infinitum. */
-  hb_blob_t *maxp_blob = c.reference_table<OT::maxp> (this);
-  const OT::maxp *maxp_table = maxp_blob->as<OT::maxp> ();
+  unsigned ret = 0;
 
-  unsigned int ret = maxp_table->get_num_glyphs ();
+#ifndef HB_NO_BORING_EXPANSION
+  ret = hb_max (ret, load_num_glyphs_from_loca (this));
+#endif
+
+  ret = hb_max (ret, load_num_glyphs_from_maxp (this));
+
   num_glyphs.set_relaxed (ret);
-  hb_blob_destroy (maxp_blob);
   return ret;
 }
 
diff --git a/subprojects/freetype2.wrap b/subprojects/freetype2.wrap
index cf8f307..43ab8fa 100644
--- a/subprojects/freetype2.wrap
+++ b/subprojects/freetype2.wrap
@@ -5,3 +5,4 @@
 
 [provide]
 freetype2 = freetype_dep
+freetype = freetype_dep
diff --git a/test/api/Makefile.am b/test/api/Makefile.am
index ee662cd..a2412f9 100644
--- a/test/api/Makefile.am
+++ b/test/api/Makefile.am
@@ -30,6 +30,8 @@
 TEST_PROGS = \
 	test-aat-layout \
 	test-baseline \
+	test-be-glyph-advance \
+	test-be-num-glyphs \
 	test-blob \
 	test-buffer \
 	test-c \
diff --git a/test/api/hb-test.h b/test/api/hb-test.h
index 7390e57..840637a 100644
--- a/test/api/hb-test.h
+++ b/test/api/hb-test.h
@@ -52,6 +52,17 @@
 				  ((const char *) s)[2], \
 				  ((const char *) s)[3]))
 
+#define HB_FACE_ADD_TABLE(face, tag, data) \
+	do { \
+	  hb_blob_t *blob = hb_blob_create_or_fail ((data), \
+						    sizeof (data), \
+						    HB_MEMORY_MODE_READONLY, \
+						    NULL, NULL); \
+	  hb_face_builder_add_table ((face), \
+				     HB_TAG_CHAR4(tag), \
+				     blob); \
+	  hb_blob_destroy (blob); \
+	} while (0)
 
 static inline const char *
 srcdir (void)
diff --git a/test/api/meson.build b/test/api/meson.build
index 7ea2954..ab64338 100644
--- a/test/api/meson.build
+++ b/test/api/meson.build
@@ -6,6 +6,8 @@
 tests = [
   'test-aat-layout.c',
   'test-baseline.c',
+  'test-be-glyph-advance.c',
+  'test-be-num-glyphs.c',
   'test-blob.c',
   'test-buffer.c',
   'test-c.c',
diff --git a/test/api/test-be-glyph-advance.c b/test/api/test-be-glyph-advance.c
new file mode 100644
index 0000000..77fdee1
--- /dev/null
+++ b/test/api/test-be-glyph-advance.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright © 2022  Behdad Esfahbod
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#include "hb-test.h"
+
+#include <hb.h>
+
+static void
+test_maxp_and_hmtx (void)
+{
+  hb_face_t *face;
+  hb_font_t *font;
+
+  const char maxp_data[] = "\x00\x00\x50\x00" // version
+			   "\x00\x05" // numGlyphs
+			   ;
+  const char loca_data[18] = "";
+  const char hhea_data[36] =
+    "\x00\x01\x00\x00" /* FixedVersion<>version;	 * 0x00010000u for version 1.0. */
+    "\x02\x00" /* FWORD		ascender;	 * Typographic ascent. */
+    "\x00\x10" /* FWORD		descender;	 * Typographic descent. */
+    "\x00\x00" /* FWORD		lineGap;	 * Typographic line gap. */
+    "\x00\x00" /* UFWORD	advanceMax;	 * Maximum advance width/height value in metrics table. */
+    "\x00\x00" /* FWORD		minLeadingBearing;  * Minimum left/top sidebearing value in metrics table. */
+    "\x00\x00" /* FWORD		minTrailingBearing;  * Minimum right/bottom sidebearing value; */
+    "\x01\x00" /* FWORD		maxExtent;	 * horizontal: Max(lsb + (xMax - xMin)), */
+    "\x00\x00" /* HBINT16	caretSlopeRise;	 * Used to calculate the slope of the,*/
+    "\x00\x00" /* HBINT16	caretSlopeRun;	 * 0 for vertical caret, 1 for horizontal. */
+    "\x00\x00" /* HBINT16	caretOffset;	 * The amount by which a slanted */
+    "\x00\x00" /* HBINT16	reserved1;	 * Set to 0. */
+    "\x00\x00" /* HBINT16	reserved2;	 * Set to 0. */
+    "\x00\x00" /* HBINT16	reserved3;	 * Set to 0. */
+    "\x00\x00" /* HBINT16	reserved4;	 * Set to 0. */
+    "\x00\x00" /* HBINT16	metricDataFormat; * 0 for current format. */
+    "\x00\x02" /* HBUINT16	numberOfLongMetrics;  * Number of LongMetric entries in metric table. */
+    ;
+  const char hmtx_data[18] =
+    "\x00\x01\x00\x02"	/* glyph 0 advance lsb */
+    "\x00\x03\x00\x04"	/* glyph 1 advance lsb */
+    "\x00\x05"		/* glyph 2         lsb */
+    "\x00\x06"		/* glyph 3         lsb */
+    "\x00\x07"		/* glyph 4         lsb */
+    "\x00\x08"		/* glyph 5 advance */
+    "\x00\x09"		/* glyph 6 advance */
+    ;
+
+  face = hb_face_builder_create ();
+  HB_FACE_ADD_TABLE (face, "maxp", maxp_data);
+  HB_FACE_ADD_TABLE (face, "loca", loca_data);
+  HB_FACE_ADD_TABLE (face, "hhea", hhea_data);
+  HB_FACE_ADD_TABLE (face, "hmtx", hmtx_data);
+  font = hb_font_create (face);
+  hb_face_destroy (face);
+  g_assert_cmpuint (hb_font_get_glyph_h_advance (font, 0), ==, 1);
+  g_assert_cmpuint (hb_font_get_glyph_h_advance (font, 1), ==, 3);
+  g_assert_cmpuint (hb_font_get_glyph_h_advance (font, 2), ==, 3);
+  g_assert_cmpuint (hb_font_get_glyph_h_advance (font, 3), ==, 3);
+  g_assert_cmpuint (hb_font_get_glyph_h_advance (font, 4), ==, 3);
+#ifndef HB_NO_BORING_EXPANSION
+  g_assert_cmpuint (hb_font_get_glyph_h_advance (font, 5), ==, 8);
+  g_assert_cmpuint (hb_font_get_glyph_h_advance (font, 6), ==, 9);
+  g_assert_cmpuint (hb_font_get_glyph_h_advance (font, 7), ==, 9);
+#endif
+  g_assert_cmpuint (hb_font_get_glyph_h_advance (font, 8), ==, 0);
+  g_assert_cmpuint (hb_font_get_glyph_h_advance (font, 9), ==, 0);
+  g_assert_cmpuint (hb_font_get_glyph_h_advance (font,10), ==, 0);
+  g_assert_cmpuint (hb_font_get_glyph_h_advance (font,11), ==, 0);
+  hb_font_destroy (font);
+}
+
+
+int
+main (int argc, char **argv)
+{
+  hb_test_init (&argc, &argv);
+
+  hb_test_add (test_maxp_and_hmtx);
+
+  return hb_test_run();
+}
diff --git a/test/api/test-be-num-glyphs.c b/test/api/test-be-num-glyphs.c
new file mode 100644
index 0000000..ab8d110
--- /dev/null
+++ b/test/api/test-be-num-glyphs.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright © 2022  Behdad Esfahbod
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#include "hb-test.h"
+
+#include <hb.h>
+
+static void
+test_maxp_and_loca (void)
+{
+  hb_face_t *face;
+
+  const char maxp_data[] = "\x00\x00\x50\x00" // version
+			   "\x00\x05" // numGlyphs
+			   ;
+  const char loca_data[18] = "";
+
+  face = hb_face_builder_create ();
+  g_assert_cmpuint (hb_face_get_glyph_count (face), ==, 0);
+  hb_face_destroy (face);
+
+  face = hb_face_builder_create ();
+  HB_FACE_ADD_TABLE (face, "maxp", maxp_data);
+  g_assert_cmpuint (hb_face_get_glyph_count (face), ==, 5);
+  hb_face_destroy (face);
+
+#ifndef HB_NO_BORING_EXPANSION
+  face = hb_face_builder_create ();
+  HB_FACE_ADD_TABLE (face, "maxp", maxp_data);
+  HB_FACE_ADD_TABLE (face, "loca", loca_data);
+  g_assert_cmpuint (hb_face_get_glyph_count (face), ==, 8);
+  hb_face_destroy (face);
+
+  face = hb_face_builder_create ();
+  HB_FACE_ADD_TABLE (face, "loca", loca_data);
+  g_assert_cmpuint (hb_face_get_glyph_count (face), ==, 8);
+  hb_face_destroy (face);
+#endif
+}
+
+
+int
+main (int argc, char **argv)
+{
+  hb_test_init (&argc, &argv);
+
+  hb_test_add (test_maxp_and_loca);
+
+  return hb_test_run();
+}