diff --git a/src/font.h b/src/font.h
index 3540a8dba22..1f62a61f0be 100644
--- a/src/font.h
+++ b/src/font.h
@@ -22,6 +22,10 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
 #ifndef EMACS_FONT_H
 #define EMACS_FONT_H
 
+#ifdef HAVE_HARFBUZZ
+#include <hb.h>
+#endif	/* HAVE_HARFBUZZ */
+
 struct composition_it;
 struct face;
 struct glyph_string;
@@ -780,6 +784,21 @@ struct font_driver
      relies on this hook to throw away its old XftDraw (which won't
      work after the size change) and get a new one.  */
   void (*drop_xrender_surfaces) (struct frame *f);
+
+#ifdef HAVE_HARFBUZZ
+  /* Optional.
+     Return a HarfBuzz font object for FONT and store to
+     *POSITION_UNIT the scale factor to convert a hb_position_t value
+     to the number of pixels.  Return NULL if HarfBuzz font object is
+     not available for FONT.  */
+  hb_font_t *(*begin_hb_font) (struct font *font, double *position_unit);
+
+  /* Optional.
+     Called when the return value (passed as HB_FONT) of begin_hb_font
+     above is no longer used.  Not called if the return value of
+     begin_hb_font was NULL.  */
+  void (*end_hb_font) (struct font *font, hb_font_t *hb_font);
+#endif	/* HAVE_HARFBUZZ */
 };
 
 
@@ -892,9 +911,9 @@ extern int ftfont_has_char (Lisp_Object, int);
 extern int ftfont_variation_glyphs (struct font *, int, unsigned[256]);
 extern Lisp_Object ftfont_combining_capability (struct font *);
 extern Lisp_Object ftfont_get_cache (struct frame *);
-extern Lisp_Object ftfont_list (struct frame *, Lisp_Object);
+extern Lisp_Object ftfont_list2 (struct frame *, Lisp_Object, Lisp_Object);
 extern Lisp_Object ftfont_list_family (struct frame *);
-extern Lisp_Object ftfont_match (struct frame *, Lisp_Object);
+extern Lisp_Object ftfont_match2 (struct frame *, Lisp_Object, Lisp_Object);
 extern Lisp_Object ftfont_open (struct frame *, Lisp_Object, int);
 extern Lisp_Object ftfont_otf_capability (struct font *);
 extern Lisp_Object ftfont_shape (Lisp_Object, Lisp_Object);
@@ -903,6 +922,11 @@ extern void ftfont_close (struct font *);
 extern void ftfont_filter_properties (Lisp_Object, Lisp_Object);
 extern void ftfont_text_extents (struct font *, unsigned *, int,
 				 struct font_metrics *);
+#ifdef HAVE_HARFBUZZ
+extern Lisp_Object fthbfont_combining_capability (struct font *);
+extern hb_font_t *fthbfont_begin_hb_font (struct font *, double *);
+extern Lisp_Object fthbfont_shape (Lisp_Object, Lisp_Object);
+#endif	/* HAVE_HARFBUZZ */
 extern void syms_of_ftfont (void);
 #endif	/* HAVE_FREETYPE */
 #ifdef HAVE_X_WINDOWS
@@ -912,6 +936,9 @@ extern void syms_of_xfont (void);
 extern void syms_of_ftxfont (void);
 #ifdef HAVE_XFT
 extern struct font_driver const xftfont_driver;
+#ifdef HAVE_HARFBUZZ
+extern struct font_driver xfthbfont_driver;
+#endif	/* HAVE_HARFBUZZ */
 #endif
 #if defined HAVE_FREETYPE || defined HAVE_XFT
 extern struct font_driver const ftxfont_driver;
@@ -933,6 +960,9 @@ extern void syms_of_macfont (void);
 #endif	/* HAVE_NS */
 #ifdef USE_CAIRO
 extern struct font_driver const ftcrfont_driver;
+#ifdef HAVE_HARFBUZZ
+extern struct font_driver ftcrhbfont_driver;
+#endif	/* HAVE_HARFBUZZ */
 extern void syms_of_ftcrfont (void);
 #endif
 
diff --git a/src/ftcrfont.c b/src/ftcrfont.c
index c0f62e0418e..dc59c2bcadc 100644
--- a/src/ftcrfont.c
+++ b/src/ftcrfont.c
@@ -98,21 +98,13 @@ ftcrfont_glyph_extents (struct font *font,
 static Lisp_Object
 ftcrfont_list (struct frame *f, Lisp_Object spec)
 {
-  Lisp_Object list = ftfont_list (f, spec), tail;
-
-  for (tail = list; CONSP (tail); tail = XCDR (tail))
-    ASET (XCAR (tail), FONT_TYPE_INDEX, Qftcr);
-  return list;
+  return ftfont_list2 (f, spec, Qftcr);
 }
 
 static Lisp_Object
 ftcrfont_match (struct frame *f, Lisp_Object spec)
 {
-  Lisp_Object entity = ftfont_match (f, spec);
-
-  if (VECTORP (entity))
-    ASET (entity, FONT_TYPE_INDEX, Qftcr);
-  return entity;
+  return ftfont_match2 (f, spec, Qftcr);
 }
 
 static Lisp_Object
@@ -124,7 +116,8 @@ ftcrfont_open (struct frame *f, Lisp_Object entity, int pixel_size)
   if (size == 0)
     size = pixel_size;
   font_object = font_build_object (VECSIZE (struct font_info),
-				   Qftcr, entity, size);
+				   AREF (entity, FONT_TYPE_INDEX),
+				   entity, size);
   block_input ();
   font_object = ftfont_open2 (f, entity, pixel_size, font_object);
   if (FONT_OBJECT_P (font_object))
@@ -133,6 +126,11 @@ ftcrfont_open (struct frame *f, Lisp_Object entity, int pixel_size)
       struct font_info *ftcrfont_info = (struct font_info *) font;
       FT_Face ft_face = ftcrfont_info->ft_size->face;
 
+#ifdef HAVE_HARFBUZZ
+      if (EQ (AREF (font_object, FONT_TYPE_INDEX), Qftcrhb))
+	font->driver = &ftcrhbfont_driver;
+      else
+#endif	/* HAVE_HARFBUZZ */
       font->driver = &ftcrfont_driver;
       FT_New_Size (ft_face, &ftcrfont_info->ft_size_draw);
       FT_Activate_Size (ftcrfont_info->ft_size_draw);
@@ -291,7 +289,7 @@ ftcrfont_anchor_point (struct font *font, unsigned int code, int idx,
 static Lisp_Object
 ftcrfont_shape (Lisp_Object lgstring, Lisp_Object direction)
 {
-#if (defined HAVE_M17N_FLT && defined HAVE_LIBOTF) || defined HAVE_HARFBUZZ
+#if defined HAVE_M17N_FLT && defined HAVE_LIBOTF
   struct font *font = CHECK_FONT_GET_OBJECT (LGSTRING_FONT (lgstring));
   struct font_info *ftcrfont_info = (struct font_info *) font;
 
@@ -361,6 +359,39 @@ ftcrfont_draw (struct glyph_string *s,
   return len;
 }
 
+#ifdef HAVE_HARFBUZZ
+
+static Lisp_Object
+ftcrhbfont_list (struct frame *f, Lisp_Object spec)
+{
+  return ftfont_list2 (f, spec, Qftcrhb);
+}
+
+static Lisp_Object
+ftcrhbfont_match (struct frame *f, Lisp_Object spec)
+{
+  return ftfont_match2 (f, spec, Qftcrhb);
+}
+
+static hb_font_t *
+ftcrhbfont_begin_hb_font (struct font *font, double *position_unit)
+{
+  struct font_info *ftcrfont_info = (struct font_info *) font;
+
+  FT_Activate_Size (ftcrfont_info->ft_size_draw);
+  hb_font_t *hb_font = fthbfont_begin_hb_font (font, position_unit);
+  int i = ftcrfont_info->bitmap_strike_index;
+  if (i >= 0)
+    {
+      FT_Face ft_face = ftcrfont_info->ft_size_draw->face;
+      *position_unit = ((double) font->height
+			/ ft_face->available_sizes[i].height) / (1 << 6);
+    }
+
+  return hb_font;
+}
+
+#endif	/* HAVE_HARFBUZZ */
 
 
 static void syms_of_ftcrfont_for_pdumper (void);
@@ -390,11 +421,17 @@ struct font_driver const ftcrfont_driver =
   .filter_properties = ftfont_filter_properties,
   .combining_capability = ftfont_combining_capability,
   };
+#ifdef HAVE_HARFBUZZ
+struct font_driver ftcrhbfont_driver;
+#endif	/* HAVE_HARFBUZZ */
 
 void
 syms_of_ftcrfont (void)
 {
   DEFSYM (Qftcr, "ftcr");
+#ifdef HAVE_HARFBUZZ
+  DEFSYM (Qftcrhb, "ftcrhb");
+#endif	/* HAVE_HARFBUZZ */
   pdumper_do_now_and_after_load (syms_of_ftcrfont_for_pdumper);
 }
 
@@ -402,4 +439,14 @@ static void
 syms_of_ftcrfont_for_pdumper (void)
 {
   register_font_driver (&ftcrfont_driver, NULL);
+#ifdef HAVE_HARFBUZZ
+  ftcrhbfont_driver = ftcrfont_driver;
+  ftcrhbfont_driver.type = Qftcrhb;
+  ftcrhbfont_driver.list = ftcrhbfont_list;
+  ftcrhbfont_driver.match = ftcrhbfont_match;
+  ftcrhbfont_driver.shape = fthbfont_shape;
+  ftcrhbfont_driver.combining_capability = fthbfont_combining_capability;
+  ftcrhbfont_driver.begin_hb_font = ftcrhbfont_begin_hb_font;
+  register_font_driver (&ftcrhbfont_driver, NULL);
+#endif	/* HAVE_HARFBUZZ */
 }
diff --git a/src/ftfont.c b/src/ftfont.c
index 58c462a90fe..718fae2b87b 100644
--- a/src/ftfont.c
+++ b/src/ftfont.c
@@ -21,6 +21,7 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
 
 #include <config.h>
 #include <stdio.h>
+#include <math.h>
 #include <fontconfig/fontconfig.h>
 #include <fontconfig/fcfreetype.h>
 
@@ -48,6 +49,9 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
 #include "pdumper.h"
 
 static struct font_driver const ftfont_driver;
+#ifdef HAVE_HARFBUZZ
+static struct font_driver fthbfont_driver;
+#endif	/* HAVE_HARFBUZZ */
 
 /* Flag to tell if FcInit is already called or not.  */
 static bool fc_initialized;
@@ -466,19 +470,6 @@ ftfont_get_otf (struct font_info *ftfont_info)
 }
 #endif	/* HAVE_LIBOTF */
 
-#ifdef HAVE_HARFBUZZ
-
-static hb_font_t *
-ftfont_get_hb_font (struct font_info *ftfont_info)
-{
-  if (! ftfont_info->hb_font)
-    ftfont_info->hb_font
-      = hb_ft_font_create_referenced (ftfont_info->ft_size->face);
-  return ftfont_info->hb_font;
-}
-
-#endif	/* HAVE_HARFBUZZ */
-
 Lisp_Object
 ftfont_get_cache (struct frame *f)
 {
@@ -801,7 +792,7 @@ ftfont_spec_pattern (Lisp_Object spec, char *otlayout, struct OpenTypeSpec **ots
   return pattern;
 }
 
-Lisp_Object
+static Lisp_Object
 ftfont_list (struct frame *f, Lisp_Object spec)
 {
   Lisp_Object val = Qnil, family, adstyle;
@@ -1001,6 +992,16 @@ ftfont_list (struct frame *f, Lisp_Object spec)
 }
 
 Lisp_Object
+ftfont_list2 (struct frame *f, Lisp_Object spec, Lisp_Object type)
+{
+  Lisp_Object list = ftfont_list (f, spec);
+
+  for (Lisp_Object tail = list; CONSP (tail); tail = XCDR (tail))
+    ASET (XCAR (tail), FONT_TYPE_INDEX, type);
+  return list;
+}
+
+static Lisp_Object
 ftfont_match (struct frame *f, Lisp_Object spec)
 {
   Lisp_Object entity = Qnil;
@@ -1050,6 +1051,16 @@ ftfont_match (struct frame *f, Lisp_Object spec)
   return entity;
 }
 
+Lisp_Object
+ftfont_match2 (struct frame *f, Lisp_Object spec, Lisp_Object type)
+{
+  Lisp_Object entity = ftfont_match (f, spec);
+
+  if (! NILP (entity))
+    ASET (entity, FONT_TYPE_INDEX, type);
+  return entity;
+}
+
 Lisp_Object
 ftfont_list_family (struct frame *f)
 {
@@ -1185,6 +1196,11 @@ ftfont_open2 (struct frame *f,
   /* This means that there's no need of transformation.  */
   ftfont_info->matrix.xx = 0;
   font->pixel_size = size;
+#ifdef HAVE_HARFBUZZ
+  if (EQ (AREF (font_object, FONT_TYPE_INDEX), Qfreetypehb))
+    font->driver = &fthbfont_driver;
+  else
+#endif	/* HAVE_HARFBUZZ */
   font->driver = &ftfont_driver;
   font->encoding_charset = font->repertory_charset = -1;
 
@@ -1266,7 +1282,8 @@ ftfont_open (struct frame *f, Lisp_Object entity, int pixel_size)
   if (size == 0)
     size = pixel_size;
   font_object = font_build_object (VECSIZE (struct font_info),
-				   Qfreetype, entity, size);
+				   AREF (entity, FONT_TYPE_INDEX),
+				   entity, size);
   font_object = ftfont_open2 (f, entity, pixel_size, font_object);
   if (FONT_OBJECT_P (font_object))
     {
@@ -2673,6 +2690,17 @@ ftfont_shape_by_flt (Lisp_Object lgstring, struct font *font,
   return make_fixnum (i);
 }
 
+Lisp_Object
+ftfont_shape (Lisp_Object lgstring)
+{
+  struct font *font = CHECK_FONT_GET_OBJECT (LGSTRING_FONT (lgstring));
+  struct font_info *ftfont_info = (struct font_info *) font;
+  OTF *otf = ftfont_get_otf (ftfont_info);
+
+  return ftfont_shape_by_flt (lgstring, font, ftfont_info->ft_size->face, otf,
+			      &ftfont_info->matrix);
+}
+
 #endif	/* HAVE_M17N_FLT */
 
 #ifdef HAVE_OTF_GET_VARIATION_GLYPHS
@@ -2693,6 +2721,18 @@ ftfont_variation_glyphs (struct font *font, int c, unsigned variations[256])
 
 #ifdef HAVE_HARFBUZZ
 
+hb_font_t *
+fthbfont_begin_hb_font (struct font *font, double *position_unit)
+{
+  struct font_info *ftfont_info = (struct font_info *) font;
+
+  *position_unit = 1.0 / (1 << 6);
+  if (! ftfont_info->hb_font)
+    ftfont_info->hb_font
+      = hb_ft_font_create_referenced (ftfont_info->ft_size->face);
+  return ftfont_info->hb_font;
+}
+
 static hb_unicode_combining_class_t
 uni_combining (hb_unicode_funcs_t *funcs, hb_codepoint_t ch, void *user_data)
 {
@@ -2818,8 +2858,8 @@ get_hb_unicode_funcs (void)
 }
 
 static Lisp_Object
-ftfont_shape_by_hb (Lisp_Object lgstring, FT_Face ft_face, hb_font_t *hb_font,
-                    FT_Matrix *matrix, Lisp_Object direction)
+ftfont_shape_by_hb (Lisp_Object lgstring, struct font *font,
+		    Lisp_Object direction)
 {
   ptrdiff_t glyph_len = 0, text_len = LGSTRING_GLYPH_LEN (lgstring);
   ptrdiff_t i;
@@ -2892,7 +2932,15 @@ ftfont_shape_by_hb (Lisp_Object lgstring, FT_Face ft_face, hb_font_t *hb_font,
      above. FIXME: drop once script handling is fixed above. */
   hb_buffer_guess_segment_properties (hb_buffer);
 
-  if (!hb_shape_full (hb_font, hb_buffer, NULL, 0, NULL))
+  double position_unit;
+  hb_font_t *hb_font = font->driver->begin_hb_font (font, &position_unit);
+  if (!hb_font)
+    return make_fixnum (0);
+
+  hb_bool_t success = hb_shape_full (hb_font, hb_buffer, NULL, 0, NULL);
+  if (font->driver->end_hb_font)
+    font->driver->end_hb_font (font, hb_font);
+  if (!success)
     return Qnil;
 
   glyph_len = hb_buffer_get_length (hb_buffer);
@@ -2913,7 +2961,8 @@ ftfont_shape_by_hb (Lisp_Object lgstring, FT_Face ft_face, hb_font_t *hb_font,
     {
       Lisp_Object lglyph = LGSTRING_GLYPH (lgstring, i);
       EMACS_INT from, to;
-      int advance = 0, lbearing, rbearing, ascent, descent;
+      struct font_metrics metrics = {.width = 0};
+      int xoff, yoff, wadjust;
       ptrdiff_t j = i;
 
       if (NILP (lglyph))
@@ -2936,61 +2985,46 @@ ftfont_shape_by_hb (Lisp_Object lgstring, FT_Face ft_face, hb_font_t *hb_font,
       LGLYPH_SET_CHAR (lglyph, LGLYPH_CHAR (LGSTRING_GLYPH (lgstring, from)));
       LGLYPH_SET_CODE (lglyph, info[i].codepoint);
 
-      if (ftfont_glyph_metrics (ft_face, info[i].codepoint, &advance, &lbearing,
-                                &rbearing, &ascent, &descent))
-        {
-          LGLYPH_SET_WIDTH (lglyph, advance);
-          LGLYPH_SET_LBEARING (lglyph, lbearing);
-          LGLYPH_SET_RBEARING (lglyph, rbearing);
-          LGLYPH_SET_ASCENT (lglyph, ascent);
-          LGLYPH_SET_DESCENT (lglyph, descent);
-        }
-
-      if (pos[i].x_offset || pos[i].y_offset ||
-          (pos[i].x_advance >> 6) != advance)
-      {
-        Lisp_Object vec = make_uninit_vector (3);
-        ASET (vec, 0, make_fixnum (pos[i].x_offset >> 6));
-        ASET (vec, 1, make_fixnum (-(pos[i].y_offset >> 6)));
-        ASET (vec, 2, make_fixnum (pos[i].x_advance >> 6));
-        LGLYPH_SET_ADJUSTMENT (lglyph, vec);
-      }
+      unsigned code = info[i].codepoint;
+      font->driver->text_extents (font, &code, 1, &metrics);
+      LGLYPH_SET_WIDTH (lglyph, metrics.width);
+      LGLYPH_SET_LBEARING (lglyph, metrics.lbearing);
+      LGLYPH_SET_RBEARING (lglyph, metrics.rbearing);
+      LGLYPH_SET_ASCENT (lglyph, metrics.ascent);
+      LGLYPH_SET_DESCENT (lglyph, metrics.descent);
+
+      xoff = lround (pos[i].x_offset * position_unit);
+      yoff = - lround (pos[i].y_offset * position_unit);
+      wadjust = lround (pos[i].x_advance * position_unit);
+      if (xoff || yoff || wadjust != metrics.width)
+	{
+	  Lisp_Object vec = make_uninit_vector (3);
+	  ASET (vec, 0, make_fixnum (xoff));
+	  ASET (vec, 1, make_fixnum (yoff));
+	  ASET (vec, 2, make_fixnum (wadjust));
+	  LGLYPH_SET_ADJUSTMENT (lglyph, vec);
+	}
     }
 
   return make_fixnum (glyph_len);
 }
 
-#endif /* HAVE_HARFBUZZ */
-
-#if (defined HAVE_M17N_FLT && defined HAVE_LIBOTF) || defined HAVE_HARFBUZZ
+Lisp_Object
+fthbfont_combining_capability (struct font *font)
+{
+  return Qt;
+}
 
 Lisp_Object
-ftfont_shape (Lisp_Object lgstring, Lisp_Object direction)
+fthbfont_shape (Lisp_Object lgstring, Lisp_Object direction)
 {
   struct font *font = CHECK_FONT_GET_OBJECT (LGSTRING_FONT (lgstring));
   struct font_info *ftfont_info = (struct font_info *) font;
-#ifdef HAVE_HARFBUZZ
-  if (getenv ("EMACS_NO_HARFBUZZ") == NULL)
-    {
-      hb_font_t *hb_font = ftfont_get_hb_font (ftfont_info);
-
-      return ftfont_shape_by_hb (lgstring, ftfont_info->ft_size->face,
-				 hb_font, &ftfont_info->matrix, direction);
-    }
-  else
-#endif  /* HAVE_HARFBUZZ */
-    {
-#if defined HAVE_M17N_FLT && defined HAVE_LIBOTF
-      OTF *otf = ftfont_get_otf (ftfont_info);
 
-      return ftfont_shape_by_flt (lgstring, font, ftfont_info->ft_size->face,
-				  otf, &ftfont_info->matrix);
-#endif  /* defined HAVE_M17N_FLT && defined HAVE_LIBOTF */
-    }
-  return make_fixnum (0);
+  return ftfont_shape_by_hb (lgstring, font, direction);
 }
 
-#endif /* (defined HAVE_M17N_FLT && defined HAVE_LIBOTF) || defined HAVE_HARFBUZZ */
+#endif /* HAVE_HARFBUZZ */
 
 static const char *const ftfont_booleans [] = {
   ":antialias",
@@ -3046,7 +3080,7 @@ ftfont_filter_properties (Lisp_Object font, Lisp_Object alist)
 Lisp_Object
 ftfont_combining_capability (struct font *font)
 {
-#if defined HAVE_M17N_FLT || defined HAVE_HARFBUZZ
+#ifdef HAVE_M17N_FLT
   return Qt;
 #else
   return Qnil;
@@ -3073,7 +3107,7 @@ static struct font_driver const ftfont_driver =
 #ifdef HAVE_LIBOTF
   .otf_capability = ftfont_otf_capability,
 #endif
-#if (defined HAVE_M17N_FLT && defined HAVE_LIBOTF) || defined HAVE_HARFBUZZ
+#if defined HAVE_M17N_FLT && defined HAVE_LIBOTF
   .shape = ftfont_shape,
 #endif
 #ifdef HAVE_OTF_GET_VARIATION_GLYPHS
@@ -3082,12 +3116,18 @@ static struct font_driver const ftfont_driver =
   .filter_properties = ftfont_filter_properties,
   .combining_capability = ftfont_combining_capability,
   };
+#ifdef HAVE_HARFBUZZ
+static struct font_driver fthbfont_driver;
+#endif	/* HAVE_HARFBUZZ */
 
 void
 syms_of_ftfont (void)
 {
   /* Symbolic type of this font-driver.  */
   DEFSYM (Qfreetype, "freetype");
+#ifdef HAVE_HARFBUZZ
+  DEFSYM (Qfreetypehb, "freetypehb");
+#endif	/* HAVE_HARFBUZZ */
 
   /* Fontconfig's generic families and their aliases.  */
   DEFSYM (Qmonospace, "monospace");
@@ -3114,4 +3154,12 @@ syms_of_ftfont_for_pdumper (void)
 {
   PDUMPER_RESET_LV (ft_face_cache, Qnil);
   register_font_driver (&ftfont_driver, NULL);
+#ifdef HAVE_HARFBUZZ
+  fthbfont_driver = ftfont_driver;
+  fthbfont_driver.type = Qfreetypehb;
+  fthbfont_driver.shape = fthbfont_shape;
+  fthbfont_driver.combining_capability = fthbfont_combining_capability;
+  fthbfont_driver.begin_hb_font = fthbfont_begin_hb_font;
+  register_font_driver (&fthbfont_driver, NULL);
+#endif	/* HAVE_HARFBUZZ */
 }
diff --git a/src/ftxfont.c b/src/ftxfont.c
index a549fd723bb..949ef4c503b 100644
--- a/src/ftxfont.c
+++ b/src/ftxfont.c
@@ -209,21 +209,13 @@ ftxfont_draw_background (struct frame *f, struct font *font, GC gc, int x, int y
 static Lisp_Object
 ftxfont_list (struct frame *f, Lisp_Object spec)
 {
-  Lisp_Object list = ftfont_list (f, spec), tail;
-
-  for (tail = list; CONSP (tail); tail = XCDR (tail))
-    ASET (XCAR (tail), FONT_TYPE_INDEX, Qftx);
-  return list;
+  return ftfont_list2 (f, spec, Qftx);
 }
 
 static Lisp_Object
 ftxfont_match (struct frame *f, Lisp_Object spec)
 {
-  Lisp_Object entity = ftfont_match (f, spec);
-
-  if (VECTORP (entity))
-    ASET (entity, FONT_TYPE_INDEX, Qftx);
-  return entity;
+  return ftfont_match2 (f, spec, Qftx);
 }
 
 static Lisp_Object
@@ -362,7 +354,7 @@ struct font_driver const ftxfont_driver =
   .otf_capability = ftfont_otf_capability,
 #endif
   .end_for_frame = ftxfont_end_for_frame,
-#if (defined HAVE_M17N_FLT && defined HAVE_LIBOTF) || defined HAVE_HARFBUZZ
+#if defined HAVE_M17N_FLT && defined HAVE_LIBOTF
   .shape = ftfont_shape,
 #endif
 #ifdef HAVE_OTF_GET_VARIATION_GLYPHS
diff --git a/src/xfns.c b/src/xfns.c
index 2ceb55a30ad..50a430aa78c 100644
--- a/src/xfns.c
+++ b/src/xfns.c
@@ -3774,10 +3774,16 @@ This function is an internal primitive--use `make-frame' instead.  */)
 
 #ifdef USE_CAIRO
   register_font_driver (&ftcrfont_driver, f);
+#ifdef HAVE_HARFBUZZ
+  register_font_driver (&ftcrhbfont_driver, f);
+#endif	/* HAVE_HARFBUZZ */
 #else
 #ifdef HAVE_FREETYPE
 #ifdef HAVE_XFT
   register_font_driver (&xftfont_driver, f);
+#ifdef HAVE_HARFBUZZ
+  register_font_driver (&xfthbfont_driver, f);
+#endif
 #else	/* not HAVE_XFT */
   register_font_driver (&ftxfont_driver, f);
 #endif	/* not HAVE_XFT */
diff --git a/src/xftfont.c b/src/xftfont.c
index b636a759048..f7b87f96569 100644
--- a/src/xftfont.c
+++ b/src/xftfont.c
@@ -108,21 +108,13 @@ xftfont_get_colors (struct frame *f, struct face *face, GC gc,
 static Lisp_Object
 xftfont_list (struct frame *f, Lisp_Object spec)
 {
-  Lisp_Object list = ftfont_list (f, spec);
-
-  for (Lisp_Object tail = list; CONSP (tail); tail = XCDR (tail))
-    ASET (XCAR (tail), FONT_TYPE_INDEX, Qxft);
-  return list;
+  return ftfont_list2 (f, spec, Qxft);
 }
 
 static Lisp_Object
 xftfont_match (struct frame *f, Lisp_Object spec)
 {
-  Lisp_Object entity = ftfont_match (f, spec);
-
-  if (! NILP (entity))
-    ASET (entity, FONT_TYPE_INDEX, Qxft);
-  return entity;
+  return ftfont_match2 (f, spec, Qxft);
 }
 
 static FcChar8 ascii_printable[95];
@@ -311,10 +303,16 @@ xftfont_open (struct frame *f, Lisp_Object entity, int pixel_size)
   /* We should not destroy PAT here because it is kept in XFTFONT and
      destroyed automatically when XFTFONT is closed.  */
   font_object = font_build_object (VECSIZE (struct font_info),
-				   Qxft, entity, size);
+				   AREF (entity, FONT_TYPE_INDEX),
+				   entity, size);
   ASET (font_object, FONT_FILE_INDEX, filename);
   font = XFONT_OBJECT (font_object);
   font->pixel_size = size;
+#ifdef HAVE_HARFBUZZ
+  if (EQ (AREF (font_object, FONT_TYPE_INDEX), Qxfthb))
+    font->driver = &xfthbfont_driver;
+  else
+#endif	/* HAVE_HARFBUZZ */
   font->driver = &xftfont_driver;
   font->encoding_charset = font->repertory_charset = -1;
 
@@ -649,7 +647,7 @@ xftfont_draw (struct glyph_string *s, int from, int to, int x, int y,
   return len;
 }
 
-#if (defined HAVE_M17N_FLT && defined HAVE_LIBOTF) || defined HAVE_HARFBUZZ
+#if defined HAVE_M17N_FLT && defined HAVE_LIBOTF
 static Lisp_Object
 xftfont_shape (Lisp_Object lgstring, Lisp_Object direction)
 {
@@ -739,6 +737,41 @@ xftfont_cached_font_ok (struct frame *f, Lisp_Object font_object,
   return ok;
 }
 
+#ifdef HAVE_HARFBUZZ
+
+static Lisp_Object
+xfthbfont_list (struct frame *f, Lisp_Object spec)
+{
+  return ftfont_list2 (f, spec, Qxfthb);
+}
+
+static Lisp_Object
+xfthbfont_match (struct frame *f, Lisp_Object spec)
+{
+  return ftfont_match2 (f, spec, Qxfthb);
+}
+
+static hb_font_t *
+xfthbfont_begin_hb_font (struct font *font, double *position_unit)
+{
+  struct font_info *xftfont_info = (struct font_info *) font;
+  FT_Face ft_face = XftLockFace (xftfont_info->xftfont);
+
+  xftfont_info->ft_size = ft_face->size;
+
+  return fthbfont_begin_hb_font (font, position_unit);
+}
+
+static void
+xfthbfont_end_hb_font (struct font *font, hb_font_t *hb_font)
+{
+  struct font_info *xftfont_info = (struct font_info *) font;
+
+  XftUnlockFace (xftfont_info->xftfont);
+}
+
+#endif	/* HAVE_HARFBUZZ */
+
 static void syms_of_xftfont_for_pdumper (void);
 
 struct font_driver const xftfont_driver =
@@ -763,7 +796,7 @@ struct font_driver const xftfont_driver =
   .otf_capability = ftfont_otf_capability,
 #endif
   .end_for_frame = xftfont_end_for_frame,
-#if (defined HAVE_M17N_FLT && defined HAVE_LIBOTF) || defined HAVE_HARFBUZZ
+#if defined HAVE_M17N_FLT && defined HAVE_LIBOTF
   .shape = xftfont_shape,
 #endif
 #ifdef HAVE_OTF_GET_VARIATION_GLYPHS
@@ -774,11 +807,17 @@ struct font_driver const xftfont_driver =
   .combining_capability = ftfont_combining_capability,
   .drop_xrender_surfaces = xftfont_drop_xrender_surfaces,
   };
+#ifdef HAVE_HARFBUZZ
+struct font_driver xfthbfont_driver;
+#endif	/* HAVE_HARFBUZZ */
 
 void
 syms_of_xftfont (void)
 {
   DEFSYM (Qxft, "xft");
+#ifdef HAVE_HARFBUZZ
+  DEFSYM (Qxfthb, "xfthb");
+#endif	/* HAVE_HARFBUZZ */
   DEFSYM (QChinting, ":hinting");
   DEFSYM (QCautohint, ":autohint");
   DEFSYM (QChintstyle, ":hintstyle");
@@ -799,4 +838,15 @@ static void
 syms_of_xftfont_for_pdumper (void)
 {
   register_font_driver (&xftfont_driver, NULL);
+#ifdef HAVE_HARFBUZZ
+  xfthbfont_driver = xftfont_driver;
+  xfthbfont_driver.type = Qxfthb;
+  xfthbfont_driver.list = xfthbfont_list;
+  xfthbfont_driver.match = xfthbfont_match;
+  xfthbfont_driver.shape = fthbfont_shape;
+  xfthbfont_driver.combining_capability = fthbfont_combining_capability;
+  xfthbfont_driver.begin_hb_font = xfthbfont_begin_hb_font;
+  xfthbfont_driver.end_hb_font = xfthbfont_end_hb_font;
+  register_font_driver (&xfthbfont_driver, NULL);
+#endif	/* HAVE_HARFBUZZ */
 }
