emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] master 4ebfec0 1/3: Merge remote-tracking branch 'origin/h


From: Eli Zaretskii
Subject: [Emacs-diffs] master 4ebfec0 1/3: Merge remote-tracking branch 'origin/harfbuzz'
Date: Fri, 7 Jun 2019 15:08:01 -0400 (EDT)

branch: master
commit 4ebfec02f05d838e4c88d10a764c13789816d6d4
Merge: e5bc9a3 f3fddfb
Author: Eli Zaretskii <address@hidden>
Commit: Eli Zaretskii <address@hidden>

    Merge remote-tracking branch 'origin/harfbuzz'
---
 admin/nt/dist-build/build-dep-zips.py |   5 +-
 configure.ac                          | 106 +++++---
 doc/emacs/msdos.texi                  |  39 ++-
 doc/lispref/frames.texi               |  22 +-
 etc/HELLO                             |   4 +-
 etc/NEWS                              |  11 +
 lisp/composite.el                     |  26 +-
 lisp/language/ethio-util.el           |   2 +-
 lisp/language/hebrew.el               |   6 +-
 lisp/language/ind-util.el             |   3 +
 lisp/language/japanese.el             |   2 +-
 lisp/language/lao-util.el             |   6 +-
 lisp/language/misc-lang.el            |   4 +-
 lisp/language/thai-util.el            |   6 +-
 lisp/language/tv-util.el              |   2 +-
 src/Makefile.in                       |   9 +-
 src/chartab.c                         |  15 +-
 src/composite.c                       |  44 +++-
 src/composite.h                       |   3 +-
 src/font.c                            |  22 +-
 src/font.h                            |  53 +++-
 src/ftcrfont.c                        |  97 +++++--
 src/ftfont.c                          | 146 ++++++++--
 src/ftfont.h                          |   8 +
 src/ftxfont.c                         |  14 +-
 src/hbfont.c                          | 482 ++++++++++++++++++++++++++++++++++
 src/indent.c                          |   4 +-
 src/lisp.h                            |   1 +
 src/macfont.m                         |  33 ++-
 src/w32common.h                       |   4 +
 src/w32fns.c                          |  81 +++++-
 src/w32font.c                         |   1 +
 src/w32uniscribe.c                    | 376 ++++++++++++++++++++++++--
 src/xdisp.c                           |   1 +
 src/xfns.c                            |  14 +-
 src/xftfont.c                         |  88 ++++++-
 36 files changed, 1516 insertions(+), 224 deletions(-)

diff --git a/admin/nt/dist-build/build-dep-zips.py 
b/admin/nt/dist-build/build-dep-zips.py
index 5a5bfe7..f0e96f4 100755
--- a/admin/nt/dist-build/build-dep-zips.py
+++ b/admin/nt/dist-build/build-dep-zips.py
@@ -53,13 +53,14 @@ def extract_deps():
     # This list derives from the features we want Emacs to compile with.
     PKG_REQ='''mingw-w64-x86_64-giflib
 mingw-w64-x86_64-gnutls
+mingw-w64-x86_64-harfbuzz
+mingw-w64-x86_64-lcms2
 mingw-w64-x86_64-libjpeg-turbo
 mingw-w64-x86_64-libpng
 mingw-w64-x86_64-librsvg
 mingw-w64-x86_64-libtiff
 mingw-w64-x86_64-libxml2
-mingw-w64-x86_64-xpm-nox
-mingw-w64-x86_64-lcms2'''.split()
+mingw-w64-x86_64-xpm-nox'''.split()
 
     # Get a list of all dependencies needed for packages mentioned above.
     # Run `pactree -lu' for each element of $PKG_REQ.
diff --git a/configure.ac b/configure.ac
index 0f1fd5d..bd51f54 100644
--- a/configure.ac
+++ b/configure.ac
@@ -436,6 +436,7 @@ OPTION_DEFAULT_OFF([imagemagick],[compile with ImageMagick 
image support])
 OPTION_DEFAULT_ON([json], [don't compile with native JSON support])
 
 OPTION_DEFAULT_ON([xft],[don't use XFT for anti aliased fonts])
+OPTION_DEFAULT_ON([harfbuzz],[don't use HarfBuzz for text shaping])
 OPTION_DEFAULT_ON([libotf],[don't use libotf for OpenType font support])
 OPTION_DEFAULT_ON([m17n-flt],[don't use m17n-flt for text shaping])
 
@@ -3408,53 +3409,84 @@ if test "${HAVE_X11}" = "yes"; then
     fi
   fi                             # $HAVE_CAIRO != yes
 
-    HAVE_LIBOTF=no
-    if test "${HAVE_FREETYPE}" = "yes"; then
-      AC_DEFINE(HAVE_FREETYPE, 1,
-               [Define to 1 if using the freetype and fontconfig libraries.])
-      if test "${with_libotf}" != "no"; then
-       EMACS_CHECK_MODULES([LIBOTF], [libotf])
-       if test "$HAVE_LIBOTF" = "yes"; then
-         AC_DEFINE(HAVE_LIBOTF, 1, [Define to 1 if using libotf.])
-         AC_CHECK_LIB(otf, OTF_get_variation_glyphs,
-                      HAVE_OTF_GET_VARIATION_GLYPHS=yes,
-                      HAVE_OTF_GET_VARIATION_GLYPHS=no)
-         if test "${HAVE_OTF_GET_VARIATION_GLYPHS}" = "yes"; then
-           AC_DEFINE(HAVE_OTF_GET_VARIATION_GLYPHS, 1,
-                     [Define to 1 if libotf has OTF_get_variation_glyphs.])
-         fi
-          if ! $PKG_CONFIG --atleast-version=0.9.16 libotf; then
-           AC_DEFINE(HAVE_OTF_KANNADA_BUG, 1,
+  HAVE_LIBOTF=no
+  if test "${HAVE_FREETYPE}" = "yes"; then
+    AC_DEFINE(HAVE_FREETYPE, 1,
+             [Define to 1 if using the freetype and fontconfig libraries.])
+    OLD_CFLAGS=$CFLAGS
+    OLD_LIBS=$LIBS
+    CFLAGS="$CFLAGS $FREETYPE_CFLAGS"
+    LIBS="$FREETYPE_LIBS $LIBS"
+    AC_CHECK_FUNCS(FT_Face_GetCharVariantIndex)
+    CFLAGS=$OLD_CFLAGS
+    LIBS=$OLD_LIBS
+    if test "${with_libotf}" != "no"; then
+      EMACS_CHECK_MODULES([LIBOTF], [libotf])
+      if test "$HAVE_LIBOTF" = "yes"; then
+       AC_DEFINE(HAVE_LIBOTF, 1, [Define to 1 if using libotf.])
+       AC_CHECK_LIB(otf, OTF_get_variation_glyphs,
+                    HAVE_OTF_GET_VARIATION_GLYPHS=yes,
+                    HAVE_OTF_GET_VARIATION_GLYPHS=no)
+       if test "${HAVE_OTF_GET_VARIATION_GLYPHS}" = "yes"; then
+         AC_DEFINE(HAVE_OTF_GET_VARIATION_GLYPHS, 1,
+                   [Define to 1 if libotf has OTF_get_variation_glyphs.])
+       fi
+       if ! $PKG_CONFIG --atleast-version=0.9.16 libotf; then
+         AC_DEFINE(HAVE_OTF_KANNADA_BUG, 1,
 [Define to 1 if libotf is affected by https://debbugs.gnu.org/28110.])
-         fi
        fi
       fi
-    dnl FIXME should there be an error if HAVE_FREETYPE != yes?
-    dnl Does the new font backend require it, or can it work without it?
     fi
+  dnl FIXME should there be an error if HAVE_FREETYPE != yes?
+  dnl Does the new font backend require it, or can it work without it?
+  fi
 
-    HAVE_M17N_FLT=no
-    if test "${HAVE_LIBOTF}" = yes; then
-      if test "${with_m17n_flt}" != "no"; then
-       EMACS_CHECK_MODULES([M17N_FLT], [m17n-flt])
-       if test "$HAVE_M17N_FLT" = "yes"; then
-         AC_DEFINE(HAVE_M17N_FLT, 1, [Define to 1 if using libm17n-flt.])
-       fi
+  HAVE_M17N_FLT=no
+  if test "${HAVE_LIBOTF}" = yes; then
+    if test "${with_m17n_flt}" != "no"; then
+      EMACS_CHECK_MODULES([M17N_FLT], [m17n-flt])
+      if test "$HAVE_M17N_FLT" = "yes"; then
+       AC_DEFINE(HAVE_M17N_FLT, 1, [Define to 1 if using libm17n-flt.])
       fi
     fi
-else
-    HAVE_XFT=no
-    HAVE_FREETYPE=no
-    HAVE_LIBOTF=no
-    HAVE_M17N_FLT=no
+  fi
+else # "${HAVE_X11}" != "yes"
+  HAVE_XFT=no
+  HAVE_FREETYPE=no
+  HAVE_LIBOTF=no
+  HAVE_M17N_FLT=no
+fi   # "${HAVE_X11}" != "yes"
+
+HAVE_HARFBUZZ=no
+if test "${HAVE_X11}" = "yes" && test "${HAVE_FREETYPE}" = "yes" \
+        || test "${HAVE_W32}" = "yes"; then
+  if test "${with_harfbuzz}" != "no"; then
+    ### On MS-Windows we use hb_font_get_nominal_glyph, which appeared
+    ### in HarfBuzz version 1.2.3
+    if test "${HAVE_W32}" = "yes"; then
+      EMACS_CHECK_MODULES([HARFBUZZ], [harfbuzz >= 1.2.3])
+    else
+      EMACS_CHECK_MODULES([HARFBUZZ], [harfbuzz >= 0.9.42])
+    fi
+    if test "$HAVE_HARFBUZZ" = "yes"; then
+      AC_DEFINE(HAVE_HARFBUZZ, 1, [Define to 1 if using HarfBuzz.])
+      ### mingw32 and Cygwin-w32 don't use -lharfbuzz, since they load
+      ### the library dynamically.
+      if test "${HAVE_W32}" = "yes"; then
+        HARFBUZZ_LIBS=
+      fi
+    fi
+  fi
 fi
 
-### End of font-backend (under X11) section.
+### End of font-backend section.
 
 AC_SUBST(FREETYPE_CFLAGS)
 AC_SUBST(FREETYPE_LIBS)
 AC_SUBST(FONTCONFIG_CFLAGS)
 AC_SUBST(FONTCONFIG_LIBS)
+AC_SUBST(HARFBUZZ_CFLAGS)
+AC_SUBST(HARFBUZZ_LIBS)
 AC_SUBST(LIBOTF_CFLAGS)
 AC_SUBST(LIBOTF_LIBS)
 AC_SUBST(M17N_FLT_CFLAGS)
@@ -5235,8 +5267,11 @@ if test "${HAVE_X_WINDOWS}" = "yes" ; then
   elif test "$HAVE_FREETYPE" = "yes"; then
     FONT_OBJ="$FONT_OBJ ftfont.o ftxfont.o"
   fi
-  AC_SUBST(FONT_OBJ)
 fi
+if test "${HAVE_HARFBUZZ}" = "yes" ; then
+  FONT_OBJ="$FONT_OBJ hbfont.o"
+fi
+AC_SUBST(FONT_OBJ)
 AC_SUBST(XMENU_OBJ)
 AC_SUBST(XOBJ)
 AC_SUBST(FONT_OBJ)
@@ -5587,7 +5622,7 @@ Configured for '${canonical}'.
 optsep=
 emacs_config_features=
 for opt in XAW3D XPM JPEG TIFF GIF PNG RSVG CAIRO IMAGEMAGICK SOUND GPM DBUS \
-  GCONF GSETTINGS GLIB NOTIFY ACL LIBSELINUX GNUTLS LIBXML2 FREETYPE M17N_FLT \
+  GCONF GSETTINGS GLIB NOTIFY ACL LIBSELINUX GNUTLS LIBXML2 FREETYPE HARFBUZZ 
M17N_FLT \
   LIBOTF XFT ZLIB TOOLKIT_SCROLL_BARS X_TOOLKIT OLDXMENU X11 XDBE XIM \
   NS MODULES THREADS XWIDGETS LIBSYSTEMD JSON PDUMPER UNEXEC LCMS2 GMP; do
 
@@ -5647,6 +5682,7 @@ AS_ECHO(["  Does Emacs use -lXaw3d?                       
          ${HAVE_XAW3D
   Does Emacs use -lgnutls?                                ${HAVE_GNUTLS}
   Does Emacs use -lxml2?                                  ${HAVE_LIBXML2}
   Does Emacs use -lfreetype?                              ${HAVE_FREETYPE}
+  Does Emacs use HarfBuzz?                                ${HAVE_HARFBUZZ}
   Does Emacs use -lm17n-flt?                              ${HAVE_M17N_FLT}
   Does Emacs use -lotf?                                   ${HAVE_LIBOTF}
   Does Emacs use -lxft?                                   ${HAVE_XFT}
diff --git a/doc/emacs/msdos.texi b/doc/emacs/msdos.texi
index 9fc4b62..dc34904 100644
--- a/doc/emacs/msdos.texi
+++ b/doc/emacs/msdos.texi
@@ -985,21 +985,34 @@ fontconfig library used in modern Free desktops:
   The old XLFD based format is also supported for backwards compatibility.
 
 @cindex font backend selection (MS-Windows)
-  Emacs 23 and later supports a number of font backends.  Currently,
-the @code{gdi} and @code{uniscribe} backends are supported on Windows.
-The @code{gdi} font backend is available on all versions of Windows,
-and supports all fonts that are natively supported by Windows.  The
-@code{uniscribe} font backend is available on Windows 2000 and later,
-and supports TrueType and OpenType fonts.  Some languages requiring
-complex layout can only be properly supported by the Uniscribe
-backend.  By default, both backends are enabled if supported, with
-@code{uniscribe} taking priority over @code{gdi}.  To override that
-and use the GDI backend even if Uniscribe is available, invoke Emacs
-with the @kbd{-xrm Emacs.fontBackend:gdi} command-line argument, or
-add a @code{Emacs.fontBackend} resource with the value @code{gdi} in
-the Registry under either the
+  Emacs on MS-Windows supports a number of font backends.  Currently,
+the @code{gdi}, @code{uniscribe}, and @code{harfbuzz} backends are
+available.  The @code{gdi} font backend is available on all versions
+of Windows, and supports all fonts that are natively supported by
+Windows.  The @code{uniscribe} font backend is available on Windows
+2000 and later, and supports TrueType and OpenType fonts.  The
+@code{harfbuzz} font backend is available if Emacs was built with
+HarfBuzz support, and if the HarfBuzz DLL is installed on your system;
+like @code{uniscribe}, this backend supports only TrueType and
+OpenType fonts.  Some languages requiring complex layout can only be
+properly supported by the Uniscribe or HarfBuzz backends.  By default,
+two backends are enabled for each frame: @code{gdi} and either
+@code{harfbuzz} or @code{uniscribe}, depending on which one is
+available (if both are available, only @code{harfbuzz} is enabled by
+default).  The @code{harfbuzz} and @code{uniscribe} backends take
+priority over @code{gdi} when Emacs looks for a suitable font.  To
+override that and use the GDI backend even if Uniscribe is available,
+invoke Emacs with the @kbd{-xrm Emacs.fontBackend:gdi} command-line
+argument, or add a @code{Emacs.fontBackend} resource with the value
+@code{gdi} in the Registry under either the
 @samp{HKEY_CURRENT_USER\SOFTWARE\GNU\Emacs} or the
 @samp{HKEY_LOCAL_MACHINE\SOFTWARE\GNU\Emacs} key (@pxref{Resources}).
+Similarly, to use the Uniscribe backend even if HarfBuzz is available,
+use @kbd{-xrm Emacs.fontBackend:uniscribe} on the command line that
+invokes Emacs.  You can also request all the 3 backends via the
+@code{font-backend} frame parameter, but be warned that in that case
+font searches for characters for which no fonts are available on the
+system will take longer.
 
 @cindex font properties (MS Windows)
 @noindent
diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi
index 9b3e02f..336075a 100644
--- a/doc/lispref/frames.texi
+++ b/doc/lispref/frames.texi
@@ -2278,13 +2278,21 @@ variable do not take effect immediately, only when you 
specify the
 @vindex font-backend@r{, a frame parameter}
 @item font-backend
 A list of symbols, specifying the @dfn{font backends} to use for
-drawing fonts in the frame, in order of priority.  On X, there are
-currently two available font backends: @code{x} (the X core font
-driver) and @code{xft} (the Xft font driver).  On MS-Windows, there are
-currently two available font backends: @code{gdi} and
-@code{uniscribe} (@pxref{Windows Fonts,,, emacs, The GNU Emacs
-Manual}).  On other systems, there is only one available font backend,
-so it does not make sense to modify this frame parameter.
+drawing characters on the frame, in order of priority.  In Emacs built
+without Cairo drawing on X, there are currently three available font
+backends: @code{x} (the X core font driver), @code{xft} (the Xft font
+driver), and @code{xfthb} (the Xft font driver with HarfBuzz text
+shaping).  If built with the Cairo drawing, there are two available
+font backends on X: @code{ftcr} (the FreeType font driver on Cairo)
+and @code{ftcrhb} (the FreeType font driver on Cairo with HarfBuzz
+text shaping).  On MS-Windows, there are currently three available
+font backends: @code{gdi} (the core MS-Windows font driver),
+@code{uniscribe} (font driver for OTF and TTF fonts with text shaping
+by the Uniscribe engine), and @code{harfbuzz} (font driver for OTF and
+TTF fonts with HarfBuzz text shaping) (@pxref{Windows Fonts,,, emacs,
+The GNU Emacs Manual}).  On other systems, there is only one available
+font backend, so it does not make sense to modify this frame
+parameter.
 
 @vindex background-mode@r{, a frame parameter}
 @item background-mode
diff --git a/etc/HELLO b/etc/HELLO
index 33e664e..2530625 100644
--- a/etc/HELLO
+++ b/etc/HELLO
@@ -10,7 +10,7 @@ Non-ASCII examples:
   Europe: <x-charset><param>latin-iso8859-1</param>¡Hola!, Grüß Gott, Hyvää 
päivää,</x-charset><x-charset><param>latin-iso8859-15</param> Tere 
õhtust,</x-charset><x-charset><param>latin-iso8859-3</param> Bonġu
           Cześć!,</x-charset><x-charset><param>latin-iso8859-2</param> Dobrý 
den,</x-charset><x-charset><param>cyrillic-iso8859-5</param> 
Здравствуйте!,</x-charset><x-charset><param>greek-iso8859-7</param> Γειά 
σας,</x-charset><x-charset><param>mule-unicode-0100-24ff</param> 
გამარჯობა</x-charset>
   Africa:<x-charset><param>mule-unicode-0100-24ff</param> 
</x-charset><x-charset><param>ethiopic</param>ሠላም</x-charset>
-  Middle/Near East:<x-charset><param>hebrew-iso8859-8</param> 
שלום,</x-charset><x-charset><param>mule-unicode-0100-24ff</param> السّلام 
عليكم</x-charset>
+  Middle/Near East:<x-charset><param>hebrew-iso8859-8</param> 
שָׁלוֹם,</x-charset><x-charset><param>mule-unicode-0100-24ff</param> السّلام 
عليكم</x-charset>
   South Asia:<x-charset><param>mule-unicode-0100-24ff</param> નમસ્તે, नमस्ते, 
ನಮಸ್ಕಾರ, നമസ്കാരം, ଶୁଣିବେ,
               ආයුබෝවන්, வணக்கம், 
నమస్కారం,</x-charset><x-charset><param>tibetan</param> 
བཀྲ་ཤིས་བདེ་ལེགས༎</x-charset>
   South East Asia:<x-charset><param>mule-unicode-0100-24ff</param> 
ជំរាបសួរ,</x-charset><x-charset><param>lao</param> 
ສະບາຍດີ,</x-charset><x-charset><param>mule-unicode-0100-24ff</param> 
မင်္ဂလာပါ,</x-charset><x-charset><param>thai-tis620</param> 
สวัสดีครับ,</x-charset><x-charset><param>vietnamese-viscii-lower</param> 
</x-charset><x-charset><param>vietnamese-viscii-upper</param>C</x-charset><x-charset><param>vietnamese-viscii-lower</param>hào
 bạn</x-charset>
@@ -52,7 +52,7 @@ French (français)     Bonjour / Salut
 </x-charset><x-charset><param>greek-iso8859-7</param>Greek (ελληνικά)  Γειά σας
 </x-charset><x-charset><param>mule-unicode-0100-24ff</param>Greek, ancient 
(ἑλληνική)  Οὖλέ τε καὶ μέγα χαῖρε
 Gujarati (ગુજરાતી)     નમસ્તે
-</x-charset><x-charset><param>hebrew-iso8859-8</param>Hebrew (עברית)   שלום
+</x-charset><x-charset><param>hebrew-iso8859-8</param>Hebrew (עִבְרִית)        
שָׁלוֹם
 </x-charset><x-charset><param>latin-iso8859-2</param>Hungarian (magyar)        
Szép jó napot!
 </x-charset><x-charset><param>mule-unicode-0100-24ff</param>Hindi (हिंदी)      
नमस्ते / नमस्कार ।
 </x-charset><x-charset><param>unicode</param>Inuktitut (ᐃᓄᒃᑎᑐᑦ)        ᐊᐃ
diff --git a/etc/NEWS b/etc/NEWS
index e47c305..0c2f47e 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -30,6 +30,17 @@ arranges for the included mini-gmp library to be built and 
used.
 The new 'configure' option '--without-libgmp' uses mini-gmp even if a
 suitable libgmp is available.
 
+** The new configure option '--with-harfbuzz' adds support for the
+HarfBuzz text shaping engine.  It is on by default; use './configure
+--without-harfbuzz' to build without it.  The HarfBuzz text shaping is
+available via new font backend drivers 'xfthb' and 'ftcrhb' for Xft
+and Cairo drawings, respectively, and via the 'harfbuzz' backend on
+MS-Windows.  The Harfbuzz text shaping is preferred to the previously
+supported ones, so the font backends that use older shaping engines
+(FLT on GNU and Unix systems and Uniscribe on MS-Windows) are not
+enabled by default; they can be enabled via the 'font-backend' frame
+parameter or via X resources.
+
 ** The new configure option '--with-json' adds support for JSON using
 the Jansson library.  It is on by default; use './configure
 --with-json=no' to build without Jansson support.  The new JSON
diff --git a/lisp/composite.el b/lisp/composite.el
index 87f5630..e0d0721 100644
--- a/lisp/composite.el
+++ b/lisp/composite.el
@@ -382,8 +382,8 @@ This function is the default value of 
`compose-chars-after-function'."
                           (looking-at pattern))
                         (<= (match-end 0) limit))
                    (setq result
-                         (funcall func pos (match-end 0) font-obj object)))
-             (setq result (funcall func pos limit font-obj  object)))
+                         (funcall func pos (match-end 0) font-obj object nil)))
+             (setq result (funcall func pos limit font-obj  object nil)))
            (if result (setq tail nil))))))
     result))
 
@@ -524,8 +524,9 @@ after a sequence of character events."
       (setq from (1+ from)))
     gstring))
 
-(defun compose-gstring-for-graphic (gstring)
-  "Compose glyph-string GSTRING for graphic display.
+(defun compose-gstring-for-graphic (gstring direction)
+  "Compose glyph-string GSTRING under bidi DIRECTION for graphic display.
+DIRECTION is either L2R or R2L, or nil if unknown.
 Combining characters are composed with the preceding base
 character.  If the preceding character is not a base character,
 each combining character is composed as a spacing character by
@@ -559,7 +560,7 @@ All non-spacing characters have this function in
 
      ;; A base character and the following non-spacing characters.
      (t
-      (let ((gstr (font-shape-gstring gstring)))
+      (let ((gstr (font-shape-gstring gstring direction)))
        (if (and gstr
                 (> (lglyph-to (lgstring-glyph gstr 0)) 0))
            gstr
@@ -686,12 +687,12 @@ All non-spacing characters have this function in
              (setq i (1+ i))))
          gstring))))))
 
-(defun compose-gstring-for-dotted-circle (gstring)
+(defun compose-gstring-for-dotted-circle (gstring direction)
   (let* ((dc (lgstring-glyph gstring 0)) ; glyph of dotted-circle
         (dc-id (lglyph-code dc))
         (fc (lgstring-glyph gstring 1)) ; glyph of the following char
         (fc-id (lglyph-code fc))
-        (gstr (and nil (font-shape-gstring gstring))))
+        (gstr (and nil (font-shape-gstring gstring direction))))
     (if (and gstr
             (or (= (lgstring-glyph-len gstr) 1)
                 (and (= (lgstring-glyph-len gstr) 2)
@@ -742,7 +743,7 @@ All non-spacing characters have this function in
   (aset composition-function-table #x25CC
        `([,(purecopy ".\\c^") 0 compose-gstring-for-dotted-circle])))
 
-(defun compose-gstring-for-terminal (gstring)
+(defun compose-gstring-for-terminal (gstring _direction)
   "Compose glyph-string GSTRING for terminal display.
 Non-spacing characters are composed with the preceding base
 character.  If the preceding character is not a base character,
@@ -799,10 +800,11 @@ prepending a space before it."
     gstring))
 
 
-(defun auto-compose-chars (func from to font-object string)
+(defun auto-compose-chars (func from to font-object string direction)
   "Compose the characters at FROM by FUNC.
-FUNC is called with one argument GSTRING which is built for characters
-in the region FROM (inclusive) and TO (exclusive).
+FUNC is called with two arguments: GSTRING, which is built for
+characters in the region FROM (inclusive) and TO (exclusive);
+and DIRECTION, which is the bidi directionality of the characters.
 
 If the character are composed on a graphic display, FONT-OBJECT
 is a font to use.  Otherwise, FONT-OBJECT is nil, and the function
@@ -819,7 +821,7 @@ This function is the default value of 
`auto-composition-function' (which see)."
        gstring
       (or (fontp font-object 'font-object)
          (setq func 'compose-gstring-for-terminal))
-      (funcall func gstring))))
+      (funcall func gstring direction))))
 
 (put 'auto-composition-mode 'permanent-local t)
 
diff --git a/lisp/language/ethio-util.el b/lisp/language/ethio-util.el
index 04b15dd..2741538 100644
--- a/lisp/language/ethio-util.el
+++ b/lisp/language/ethio-util.el
@@ -1014,7 +1014,7 @@ With ARG, insert that many delimiters."
 ;;
 
 ;;;###autoload
-(defun ethio-composition-function (pos to font-object string)
+(defun ethio-composition-function (pos to font-object string _direction)
   (setq pos (1- pos))
   (let ((pattern "\\ce\\(፟\\|����\\)"))
     (if string
diff --git a/lisp/language/hebrew.el b/lisp/language/hebrew.el
index ca3058b..b493c29 100644
--- a/lisp/language/hebrew.el
+++ b/lisp/language/hebrew.el
@@ -152,7 +152,7 @@ Bidirectional editing is supported.")))
 ;; (3) If the font has precomposed glyphs, use them as far as
 ;; possible.  Adjust the remaining glyphs artificially.
 
-(defun hebrew-shape-gstring (gstring)
+(defun hebrew-shape-gstring (gstring direction)
   (let* ((font (lgstring-font gstring))
         (otf (font-get font :otf))
         (nchars (lgstring-char-len gstring))
@@ -172,7 +172,7 @@ Bidirectional editing is supported.")))
 
      ((or (assq 'hebr (car otf)) (assq 'hebr (cdr otf)))
       ;; FONT has OpenType features for Hebrew.
-      (font-shape-gstring gstring))
+      (font-shape-gstring gstring direction))
 
      (t
       ;; FONT doesn't have OpenType features for Hebrew.
@@ -217,7 +217,7 @@ Bidirectional editing is supported.")))
        ;; Now IDX is an index to the first non-precomposed glyph.
        ;; Adjust positions of the remaining glyphs artificially.
         (if (font-get font :combining-capability)
-            (font-shape-gstring gstring)
+            (font-shape-gstring gstring direction)
           (setq base-width (lglyph-width (lgstring-glyph gstring 0)))
           (while (< idx nglyphs)
             (setq glyph (lgstring-glyph gstring idx))
diff --git a/lisp/language/ind-util.el b/lisp/language/ind-util.el
index 2bbfdc1..89397f2 100644
--- a/lisp/language/ind-util.el
+++ b/lisp/language/ind-util.el
@@ -828,6 +828,9 @@ Returns new end position."
       (let ((pos from) newpos func (max to))
        (narrow-to-region from to)
        (while (< pos max)
+          ;; FIXME: The below seems to assume
+          ;; composition-function-table holds functions?  That is no
+          ;; longer true, since long ago.
          (setq func (aref composition-function-table (char-after pos)))
          (if (fboundp func)
              (setq newpos (funcall func pos nil)
diff --git a/lisp/language/japanese.el b/lisp/language/japanese.el
index fabeab8..7a0fd74 100644
--- a/lisp/language/japanese.el
+++ b/lisp/language/japanese.el
@@ -248,7 +248,7 @@ eucJP-ms is defined in 
<http://www.opengroup.or.jp/jvc/cde/appendix.html>."
   (define-translation-table 'unicode-to-jisx0213
     (char-table-extra-slot table 0)))
 
-(defun compose-gstring-for-variation-glyph (gstring)
+(defun compose-gstring-for-variation-glyph (gstring _direction)
   "Compose glyph-string GSTRING for graphic display.
 GSTRING must have two glyphs; the first is a glyph for a han character,
 and the second is a glyph for a variation selector."
diff --git a/lisp/language/lao-util.el b/lisp/language/lao-util.el
index 92b41e4..d253203 100644
--- a/lisp/language/lao-util.el
+++ b/lisp/language/lao-util.el
@@ -489,10 +489,10 @@ syllable.  In that case, FROM and TO are indexes to STR."
       lao-str)))
 
 ;;;###autoload
-(defun lao-composition-function (gstring)
+(defun lao-composition-function (gstring direction)
   (if (= (lgstring-char-len gstring) 1)
-      (compose-gstring-for-graphic gstring)
-    (or (font-shape-gstring gstring)
+      (compose-gstring-for-graphic gstring direction)
+    (or (font-shape-gstring gstring direction)
        (let ((glyph-len (lgstring-glyph-len gstring))
              (i 0)
              glyph)
diff --git a/lisp/language/misc-lang.el b/lisp/language/misc-lang.el
index c1aa79ca..e25e63b 100644
--- a/lisp/language/misc-lang.el
+++ b/lisp/language/misc-lang.el
@@ -101,8 +101,8 @@ thin (i.e. 1-dot width) space."
 ;; Record error in arabic-change-gstring.
 (defvar arabic-shape-log nil)
 
-(defun arabic-shape-gstring (gstring)
-  (setq gstring (font-shape-gstring gstring))
+(defun arabic-shape-gstring (gstring direction)
+  (setq gstring (font-shape-gstring gstring direction))
   (condition-case err
       (when arabic-shaper-ZWNJ-handling
         (let ((font (lgstring-font gstring))
diff --git a/lisp/language/thai-util.el b/lisp/language/thai-util.el
index d0f6649..e330395 100644
--- a/lisp/language/thai-util.el
+++ b/lisp/language/thai-util.el
@@ -225,10 +225,10 @@ positions (integers or markers) specifying the region."
   (thai-compose-region (point-min) (point-max)))
 
 ;;;###autoload
-(defun thai-composition-function (gstring)
+(defun thai-composition-function (gstring direction)
   (if (= (lgstring-char-len gstring) 1)
-      (compose-gstring-for-graphic gstring)
-    (or (font-shape-gstring gstring)
+      (compose-gstring-for-graphic gstring direction)
+    (or (font-shape-gstring gstring direction)
        (let ((glyph-len (lgstring-glyph-len gstring))
              (last-char (lgstring-char gstring
                                        (1- (lgstring-char-len gstring))))
diff --git a/lisp/language/tv-util.el b/lisp/language/tv-util.el
index a667956..7ce8ee1 100644
--- a/lisp/language/tv-util.el
+++ b/lisp/language/tv-util.el
@@ -128,7 +128,7 @@
 
 
 ;;;###autoload
-(defun tai-viet-composition-function (from to font-object string)
+(defun tai-viet-composition-function (from to font-object string _direction)
   (if string
       (if (string-match tai-viet-re string from)
          (tai-viet-compose-string from (match-end 0) string))
diff --git a/src/Makefile.in b/src/Makefile.in
index 3aab527..be76945 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -132,6 +132,8 @@ FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
 FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
 FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
 FREETYPE_LIBS = @FREETYPE_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
 LIBOTF_CFLAGS = @LIBOTF_CFLAGS@
 LIBOTF_LIBS = @LIBOTF_LIBS@
 M17N_FLT_CFLAGS = @M17N_FLT_CFLAGS@
@@ -297,6 +299,7 @@ W32_RES_LINK=@W32_RES_LINK@
 ## xfont.o ftfont.o ftxfont.o if HAVE_FREETYPE
 ## ftfont.o ftcrfont.o if USE_CAIRO
 ## else xfont.o
+## if HAVE_HARFBUZZ, hbfont.o is added regardless of the rest
 FONT_OBJ=@FONT_OBJ@
 
 ## Empty for MinGW, cm.o for the rest.
@@ -380,7 +383,7 @@ EMACS_CFLAGS=-Demacs $(MYCPPFLAGS) -I. -I$(srcdir) \
   $(XRANDR_CFLAGS) $(XINERAMA_CFLAGS) $(XFIXES_CFLAGS) $(XDBE_CFLAGS) \
   $(WEBKIT_CFLAGS) $(LCMS2_CFLAGS) \
   $(SETTINGS_CFLAGS) $(FREETYPE_CFLAGS) $(FONTCONFIG_CFLAGS) \
-  $(LIBOTF_CFLAGS) $(M17N_FLT_CFLAGS) $(DEPFLAGS) \
+  $(HARFBUZZ_CFLAGS) $(LIBOTF_CFLAGS) $(M17N_FLT_CFLAGS) $(DEPFLAGS) \
   $(LIBSYSTEMD_CFLAGS) $(JSON_CFLAGS) \
   $(LIBGNUTLS_CFLAGS) $(NOTIFY_CFLAGS) $(CAIRO_CFLAGS) \
   $(WERROR_CFLAGS)
@@ -429,7 +432,7 @@ SOME_MACHINE_OBJECTS = dosfns.o msdos.o \
   w32.o w32console.o w32cygwinx.o w32fns.o w32heap.o w32inevt.o w32notify.o \
   w32menu.o w32proc.o w32reg.o w32select.o w32term.o w32xfns.o \
   w16select.o widget.o xfont.o ftfont.o xftfont.o ftxfont.o gtkutil.o \
-  xsettings.o xgselect.o termcap.o
+  xsettings.o xgselect.o termcap.o hbfont.o
 
 ## gmalloc.o if !SYSTEM_MALLOC && !DOUG_LEA_MALLOC, else empty.
 GMALLOC_OBJ=@GMALLOC_OBJ@
@@ -520,7 +523,7 @@ LIBES = $(LIBS) $(W32_LIBS) $(LIBS_GNUSTEP) $(LIBX_BASE) 
$(LIBIMAGE) \
    $(XDBE_LIBS) \
    $(LIBXML2_LIBS) $(LIBGPM) $(LIBS_SYSTEM) $(CAIRO_LIBS) \
    $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \
-   $(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \
+   $(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(HARFBUZZ_LIBS) $(LIBOTF_LIBS) 
$(M17N_FLT_LIBS) \
    $(LIBGNUTLS_LIBS) $(LIB_PTHREAD) $(GETADDRINFO_A_LIBS) $(LCMS2_LIBS) \
    $(NOTIFY_LIBS) $(LIB_MATH) $(LIBZ) $(LIBMODULES) $(LIBSYSTEMD_LIBS) \
    $(JSON_LIBS) $(GMP_LIB)
diff --git a/src/chartab.c b/src/chartab.c
index 16017f4..bf8e34b 100644
--- a/src/chartab.c
+++ b/src/chartab.c
@@ -1321,22 +1321,25 @@ and put an element value.  */)
   return Fcdr (Fassq (prop, Vchar_code_property_alist));
 }
 
+Lisp_Object
+get_unicode_property (Lisp_Object char_table, int ch)
+{
+  Lisp_Object val = CHAR_TABLE_REF (char_table, ch);
+  uniprop_decoder_t decoder = uniprop_get_decoder (char_table);
+  return (decoder ? decoder (char_table, val) : val);
+}
+
 DEFUN ("get-unicode-property-internal", Fget_unicode_property_internal,
        Sget_unicode_property_internal, 2, 2, 0,
        doc: /* Return an element of CHAR-TABLE for character CH.
 CHAR-TABLE must be what returned by `unicode-property-table-internal'. */)
   (Lisp_Object char_table, Lisp_Object ch)
 {
-  Lisp_Object val;
-  uniprop_decoder_t decoder;
-
   CHECK_CHAR_TABLE (char_table);
   CHECK_CHARACTER (ch);
   if (! UNIPROP_TABLE_P (char_table))
     error ("Invalid Unicode property table");
-  val = CHAR_TABLE_REF (char_table, XFIXNUM (ch));
-  decoder = uniprop_get_decoder (char_table);
-  return (decoder ? decoder (char_table, val) : val);
+  return get_unicode_property (char_table, XFIXNUM (ch));
 }
 
 DEFUN ("put-unicode-property-internal", Fput_unicode_property_internal,
diff --git a/src/composite.c b/src/composite.c
index 7d7ed3f..183062d 100644
--- a/src/composite.c
+++ b/src/composite.c
@@ -865,7 +865,7 @@ fill_gstring_body (Lisp_Object gstring)
 static Lisp_Object
 autocmp_chars (Lisp_Object rule, ptrdiff_t charpos, ptrdiff_t bytepos,
               ptrdiff_t limit, struct window *win, struct face *face,
-              Lisp_Object string)
+              Lisp_Object string, Lisp_Object direction)
 {
   ptrdiff_t count = SPECPDL_INDEX ();
   Lisp_Object pos = make_fixnum (charpos);
@@ -912,8 +912,9 @@ autocmp_chars (Lisp_Object rule, ptrdiff_t charpos, 
ptrdiff_t bytepos,
       if (NILP (string))
        record_unwind_protect (restore_point_unwind,
                               build_marker (current_buffer, pt, pt_byte));
-      lgstring = safe_call (6, Vauto_composition_function, AREF (rule, 2),
-                           pos, make_fixnum (to), font_object, string);
+      lgstring = safe_call (7, Vauto_composition_function, AREF (rule, 2),
+                           pos, make_fixnum (to), font_object, string,
+                           direction);
     }
   return unbind_to (count, lgstring);
 }
@@ -1158,7 +1159,9 @@ composition_compute_stop_pos (struct composition_it 
*cmp_it, ptrdiff_t charpos,
    characters to be composed.  FACE, if non-NULL, is a base face of
    the character.  If STRING is not nil, it is a string containing the
    character to check, and CHARPOS and BYTEPOS are indices in the
-   string.  In that case, FACE must not be NULL.
+   string.  In that case, FACE must not be NULL.  BIDI_LEVEL is the bidi
+   embedding level of the current paragraph, and is used to calculate the
+   direction argument to pass to the font shaper.
 
    If the character is composed, setup members of CMP_IT (id, nglyphs,
    from, to, reversed_p), and return true.  Otherwise, update
@@ -1167,7 +1170,7 @@ composition_compute_stop_pos (struct composition_it 
*cmp_it, ptrdiff_t charpos,
 bool
 composition_reseat_it (struct composition_it *cmp_it, ptrdiff_t charpos,
                       ptrdiff_t bytepos, ptrdiff_t endpos, struct window *w,
-                      struct face *face, Lisp_Object string)
+                      signed char bidi_level, struct face *face, Lisp_Object 
string)
 {
   if (cmp_it->ch == -2)
     {
@@ -1197,13 +1200,17 @@ composition_reseat_it (struct composition_it *cmp_it, 
ptrdiff_t charpos,
   else if (w)
     {
       Lisp_Object lgstring = Qnil;
-      Lisp_Object val, elt;
+      Lisp_Object val, elt, direction = Qnil;
 
       val = CHAR_TABLE_REF (Vcomposition_function_table, cmp_it->ch);
       for (EMACS_INT i = 0; i < cmp_it->rule_idx; i++, val = XCDR (val))
        continue;
       if (charpos < endpos)
        {
+         if ((bidi_level & 1) == 0)
+           direction = QL2R;
+         else
+           direction = QR2L;
          for (; CONSP (val); val = XCDR (val))
            {
              elt = XCAR (val);
@@ -1213,7 +1220,7 @@ composition_reseat_it (struct composition_it *cmp_it, 
ptrdiff_t charpos,
              if (XFIXNAT (AREF (elt, 1)) != cmp_it->lookback)
                goto no_composition;
              lgstring = autocmp_chars (elt, charpos, bytepos, endpos,
-                                       w, face, string);
+                                       w, face, string, direction);
              if (composition_gstring_p (lgstring))
                break;
              lgstring = Qnil;
@@ -1237,8 +1244,12 @@ composition_reseat_it (struct composition_it *cmp_it, 
ptrdiff_t charpos,
              else
                bpos = CHAR_TO_BYTE (cpos);
            }
+         if ((bidi_level & 1) == 0)
+           direction = QL2R;
+         else
+           direction = QR2L;
          lgstring = autocmp_chars (elt, cpos, bpos, charpos + 1, w, face,
-                                   string);
+                                   string, direction);
          if (! composition_gstring_p (lgstring)
              || cpos + LGSTRING_CHAR_LEN (lgstring) - 1 != charpos)
            /* Composition failed or didn't cover the current
@@ -1558,7 +1569,7 @@ find_automatic_composition (ptrdiff_t pos, ptrdiff_t 
limit,
                  for (check = cur; check_pos < check.pos; )
                    BACKWARD_CHAR (check, stop);
                  *gstring = autocmp_chars (elt, check.pos, check.pos_byte,
-                                           tail, w, NULL, string);
+                                           tail, w, NULL, string, Qnil);
                  need_adjustment = 1;
                  if (NILP (*gstring))
                    {
@@ -1935,15 +1946,24 @@ Use the command `auto-composition-mode' to change this 
variable. */);
 
   DEFVAR_LISP ("auto-composition-function", Vauto_composition_function,
               doc: /* Function to call to compose characters automatically.
-This function is called from the display routine with four arguments:
-FROM, TO, WINDOW, and STRING.
+This function is called from the display engine with 6 arguments:
+FUNC, FROM, TO, FONT-OBJECT, STRING, and DIRECTION.
+
+FUNC is the function to compose characters.  On text-mode display,
+FUNC is ignored and `compose-gstring-for-terminal' is used instead.
 
 If STRING is nil, the function must compose characters in the region
 between FROM and TO in the current buffer.
 
 Otherwise, STRING is a string, and FROM and TO are indices into the
 string.  In this case, the function must compose characters in the
-string.  */);
+string.
+
+FONT-OBJECT is the font to use, or nil if characters are to be
+composed on a text-mode display.
+
+DIRECTION is the bidi directionality of the text to shape.  It could
+be L2R or R2L, or nil if unknown.  */);
   Vauto_composition_function = Qnil;
 
   DEFVAR_LISP ("composition-function-table", Vcomposition_function_table,
diff --git a/src/composite.h b/src/composite.h
index 8675163..45d72fb 100644
--- a/src/composite.h
+++ b/src/composite.h
@@ -26,6 +26,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #define EMACS_COMPOSITE_H
 
 #include "font.h"
+#include "dispextern.h"
 
 INLINE_HEADER_BEGIN
 
@@ -321,7 +322,7 @@ extern void composition_compute_stop_pos (struct 
composition_it *,
                                           Lisp_Object);
 extern bool composition_reseat_it (struct composition_it *, ptrdiff_t,
                                   ptrdiff_t, ptrdiff_t, struct window *,
-                                  struct face *, Lisp_Object);
+                                  signed char, struct face *, Lisp_Object);
 extern int composition_update_it (struct composition_it *,
                                   ptrdiff_t, ptrdiff_t, Lisp_Object);
 
diff --git a/src/font.c b/src/font.c
index 5ca89c9..6ab4923 100644
--- a/src/font.c
+++ b/src/font.c
@@ -4397,18 +4397,22 @@ font_fill_lglyph_metrics (Lisp_Object glyph, 
Lisp_Object font_object)
 }
 
 
-DEFUN ("font-shape-gstring", Ffont_shape_gstring, Sfont_shape_gstring, 1, 1, 0,
-       doc: /* Shape the glyph-string GSTRING.
+DEFUN ("font-shape-gstring", Ffont_shape_gstring, Sfont_shape_gstring, 2, 2, 0,
+       doc: /* Shape the glyph-string GSTRING subject to bidi DIRECTION.
 Shaping means substituting glyphs and/or adjusting positions of glyphs
 to get the correct visual image of character sequences set in the
 header of the glyph-string.
 
+DIRECTION should be produced by the UBA, the Unicode Bidirectional
+Algorithm, and should be a symbol, either L2R or R2L.  It can also
+be nil if the bidi context is unknown.
+
 If the shaping was successful, the value is GSTRING itself or a newly
 created glyph-string.  Otherwise, the value is nil.
 
 See the documentation of `composition-get-gstring' for the format of
 GSTRING.  */)
-  (Lisp_Object gstring)
+  (Lisp_Object gstring, Lisp_Object direction)
 {
   struct font *font;
   Lisp_Object font_object, n, glyph;
@@ -4427,7 +4431,7 @@ GSTRING.  */)
   /* Try at most three times with larger gstring each time.  */
   for (i = 0; i < 3; i++)
     {
-      n = font->driver->shape (gstring);
+      n = font->driver->shape (gstring, direction);
       if (FIXNUMP (n))
        break;
       gstring = larger_vector (gstring,
@@ -4478,11 +4482,11 @@ GSTRING.  */)
 
 DEFUN ("font-variation-glyphs", Ffont_variation_glyphs, Sfont_variation_glyphs,
        2, 2, 0,
-       doc: /* Return a list of variation glyphs for CHAR in FONT-OBJECT.
+       doc: /* Return a list of variation glyphs for CHARACTER in FONT-OBJECT.
 Each element of the value is a cons (VARIATION-SELECTOR . GLYPH-ID),
 where
-  VARIATION-SELECTOR is a character code of variation selection
-    (#xFE00..#xFE0F or #xE0100..#xE01EF)
+  VARIATION-SELECTOR is a character code of variation selector
+    (#xFE00..#xFE0F or #xE0100..#xE01EF).
   GLYPH-ID is a glyph code of the corresponding variation glyph,
 a fixnum, if it's small enough, otherwise a bignum.  */)
   (Lisp_Object font_object, Lisp_Object character)
@@ -5351,6 +5355,10 @@ syms_of_font (void)
 
   DEFSYM (QCuser_spec, ":user-spec");
 
+  /* For shapers that need to know text directionality.  */
+  DEFSYM (QL2R, "L2R");
+  DEFSYM (QR2L, "R2L");
+
   scratch_font_spec = Ffont_spec (0, NULL);
   staticpro (&scratch_font_spec);
   scratch_font_prefer = Ffont_spec (0, NULL);
diff --git a/src/font.h b/src/font.h
index 9ca0c97..1f35744 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;
@@ -703,7 +707,11 @@ struct font_driver
 
      Return the number of output codes.  If none of the features are
      applicable to the input data, return 0.  If GSTRING-OUT is too
-     short, return -1.  */
+     short, return -1.
+
+     Note: This method is currently not implemented by any font
+     back-end, and is only called by 'font-drive-otf' and
+     'font-otf-alternates', which are themselves ifdef'ed away. */
   int (*otf_drive) (struct font *font, Lisp_Object features,
                     Lisp_Object gstring_in, int from, int to,
                     Lisp_Object gstring_out, int idx, bool alternate_subst);
@@ -725,6 +733,9 @@ struct font_driver
      (N+1)th element of GSTRING is nil, input of shaping is from the
      1st to (N)th elements.  In each input glyph, FROM, TO, CHAR, and
      CODE are already set.
+     DIRECTION is either L2R or R2L, or nil if unknown.  During
+     redisplay, this comes from applying the UBA, is passed from
+     composition_reseat_it, and is used by the HarfBuzz shaper.
 
      This function updates all fields of the input glyphs.  If the
      output glyphs (M) are more than the input glyphs (N), (N+1)th
@@ -732,7 +743,7 @@ struct font_driver
      a new glyph object and storing it in GSTRING.  If (M) is greater
      than the length of GSTRING, this method should return nil.  In
      that case, the method is called again with a larger GSTRING.  */
-  Lisp_Object (*shape) (Lisp_Object lgstring);
+  Lisp_Object (*shape) (Lisp_Object lgstring, Lisp_Object direction);
 
   /* Optional.
      If FONT is usable on frame F, return 0.  Otherwise return -1.
@@ -773,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 */
 };
 
 
@@ -864,6 +890,11 @@ extern void font_fill_lglyph_metrics (Lisp_Object, 
Lisp_Object);
 extern Lisp_Object font_put_extra (Lisp_Object font, Lisp_Object prop,
                                    Lisp_Object val);
 
+#ifdef HAVE_HARFBUZZ
+extern Lisp_Object hbfont_shape (Lisp_Object, Lisp_Object);
+extern Lisp_Object hbfont_combining_capability (struct font *);
+#endif
+
 #if defined (HAVE_XFT) || defined (HAVE_FREETYPE)
 extern void font_put_frame_data (struct frame *, Lisp_Object, void *);
 extern void *font_get_frame_data (struct frame *f, Lisp_Object);
@@ -885,17 +916,20 @@ 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);
+extern Lisp_Object ftfont_shape (Lisp_Object, Lisp_Object);
 extern unsigned ftfont_encode_char (struct font *, int);
 extern void ftfont_close (struct font *);
 extern void ftfont_filter_properties (Lisp_Object, Lisp_Object);
 extern void ftfont_text_extents (struct font *, const unsigned *, int,
                                 struct font_metrics *);
+#ifdef HAVE_HARFBUZZ
+extern hb_font_t *fthbfont_begin_hb_font (struct font *, double *);
+#endif /* HAVE_HARFBUZZ */
 extern void syms_of_ftfont (void);
 #endif /* HAVE_FREETYPE */
 #ifdef HAVE_X_WINDOWS
@@ -905,6 +939,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;
@@ -917,6 +954,9 @@ extern void syms_of_bdffont (void);
 #ifdef HAVE_NTGUI
 extern struct font_driver w32font_driver;
 extern struct font_driver uniscribe_font_driver;
+#ifdef HAVE_HARFBUZZ
+extern struct font_driver harfbuzz_font_driver;
+#endif
 extern void syms_of_w32font (void);
 #endif /* HAVE_NTGUI */
 #ifdef HAVE_NS
@@ -926,6 +966,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 9686cec..1c28a7ce 100644
--- a/src/ftcrfont.c
+++ b/src/ftcrfont.c
@@ -97,21 +97,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
@@ -164,10 +156,16 @@ ftcrfont_open (struct frame *f, Lisp_Object entity, int 
pixel_size)
   unblock_input ();
 
   font_object = font_build_object (VECSIZE (struct font_info),
-                                  Qftcr, 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), Qftcrhb))
+    font->driver = &ftcrhbfont_driver;
+  else
+#endif /* HAVE_HARFBUZZ */
   font->driver = &ftcrfont_driver;
   font->encoding_charset = font->repertory_charset = -1;
 
@@ -256,6 +254,9 @@ ftcrfont_open (struct frame *f, Lisp_Object entity, int 
pixel_size)
   ftcrfont_info->maybe_otf = (ft_face->face_flags & FT_FACE_FLAG_SFNT) != 0;
   ftcrfont_info->otf = NULL;
 #endif /* HAVE_LIBOTF */
+#ifdef HAVE_HARFBUZZ
+  ftcrfont_info->hb_font = NULL;
+#endif /* HAVE_HARFBUZZ */
   if (ft_face->units_per_EM)
     ftcrfont_info->bitmap_position_unit = 0;
   else
@@ -289,6 +290,13 @@ ftcrfont_close (struct font *font)
       ftcrfont_info->otf = NULL;
     }
 #endif
+#ifdef HAVE_HARFBUZZ
+  if (ftcrfont_info->hb_font)
+    {
+      hb_font_destroy (ftcrfont_info->hb_font);
+      ftcrfont_info->hb_font = NULL;
+    }
+#endif
   for (int i = 0; i < ftcrfont_info->metrics_nrows; i++)
     if (ftcrfont_info->metrics[i])
       xfree (ftcrfont_info->metrics[i]);
@@ -427,7 +435,7 @@ ftcrfont_otf_capability (struct font *font)
 
 #if defined HAVE_M17N_FLT && defined HAVE_LIBOTF
 static Lisp_Object
-ftcrfont_shape (Lisp_Object lgstring)
+ftcrfont_shape (Lisp_Object lgstring, Lisp_Object direction)
 {
   struct font *font = CHECK_FONT_GET_OBJECT (LGSTRING_FONT (lgstring));
   struct font_info *ftcrfont_info = (struct font_info *) font;
@@ -439,7 +447,7 @@ ftcrfont_shape (Lisp_Object lgstring)
   FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font);
 
   ftcrfont_info->ft_size = ft_face->size;
-  Lisp_Object result = ftfont_shape (lgstring);
+  Lisp_Object result = ftfont_shape (lgstring, direction);
   cairo_ft_scaled_font_unlock_face (scaled_font);
   ftcrfont_info->ft_size = NULL;
 
@@ -447,7 +455,7 @@ ftcrfont_shape (Lisp_Object lgstring)
 }
 #endif
 
-#ifdef HAVE_OTF_GET_VARIATION_GLYPHS
+#if defined HAVE_OTF_GET_VARIATION_GLYPHS || defined 
HAVE_FT_FACE_GETCHARVARIANTINDEX
 static int
 ftcrfont_variation_glyphs (struct font *font, int c, unsigned variations[256])
 {
@@ -462,7 +470,7 @@ ftcrfont_variation_glyphs (struct font *font, int c, 
unsigned variations[256])
 
   return result;
 }
-#endif /* HAVE_OTF_GET_VARIATION_GLYPHS */
+#endif /* HAVE_OTF_GET_VARIATION_GLYPHS || HAVE_FT_FACE_GETCHARVARIANTINDEX */
 
 static int
 ftcrfont_draw (struct glyph_string *s,
@@ -510,6 +518,46 @@ 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;
+  cairo_scaled_font_t *scaled_font = ftcrfont_info->cr_scaled_font;
+  FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font);
+
+  ftcrfont_info->ft_size = ft_face->size;
+  hb_font_t *hb_font = fthbfont_begin_hb_font (font, position_unit);
+  if (ftcrfont_info->bitmap_position_unit)
+    *position_unit = ftcrfont_info->bitmap_position_unit;
+
+  return hb_font;
+}
+
+static void
+ftcrhbfont_end_hb_font (struct font *font, hb_font_t *hb_font)
+{
+  struct font_info *ftcrfont_info = (struct font_info *) font;
+  cairo_scaled_font_t *scaled_font = ftcrfont_info->cr_scaled_font;
+
+  cairo_ft_scaled_font_unlock_face (scaled_font);
+  ftcrfont_info->ft_size = NULL;
+}
+
+#endif /* HAVE_HARFBUZZ */
 
 
 static void syms_of_ftcrfont_for_pdumper (void);
@@ -535,17 +583,23 @@ struct font_driver const ftcrfont_driver =
 #if defined HAVE_M17N_FLT && defined HAVE_LIBOTF
   .shape = ftcrfont_shape,
 #endif
-#ifdef HAVE_OTF_GET_VARIATION_GLYPHS
+#if defined HAVE_OTF_GET_VARIATION_GLYPHS || defined 
HAVE_FT_FACE_GETCHARVARIANTINDEX
   .get_variation_glyphs = ftcrfont_variation_glyphs,
 #endif
   .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);
 }
 
@@ -553,4 +607,15 @@ 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 = hbfont_shape;
+  ftcrhbfont_driver.combining_capability = hbfont_combining_capability;
+  ftcrhbfont_driver.begin_hb_font = ftcrhbfont_begin_hb_font;
+  ftcrhbfont_driver.end_hb_font = ftcrhbfont_end_hb_font;
+  register_font_driver (&ftcrhbfont_driver, NULL);
+#endif /* HAVE_HARFBUZZ */
 }
diff --git a/src/ftfont.c b/src/ftfont.c
index d8b510d..efd0fcb 100644
--- a/src/ftfont.c
+++ b/src/ftfont.c
@@ -48,6 +48,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;
@@ -788,7 +791,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;
@@ -988,6 +991,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;
@@ -1038,6 +1051,16 @@ ftfont_match (struct frame *f, Lisp_Object spec)
 }
 
 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)
 {
   Lisp_Object list = Qnil;
@@ -1292,9 +1315,17 @@ ftfont_open (struct frame *f, Lisp_Object entity, int 
pixel_size)
   ftfont_info->maybe_otf = (ft_face->face_flags & FT_FACE_FLAG_SFNT) != 0;
   ftfont_info->otf = NULL;
 #endif /* HAVE_LIBOTF */
+#ifdef HAVE_HARFBUZZ
+  ftfont_info->hb_font = NULL;
+#endif /* HAVE_HARFBUZZ */
   /* 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;
 
@@ -1407,6 +1438,10 @@ ftfont_close (struct font *font)
       if (ftfont_info->otf)
        OTF_close (ftfont_info->otf);
 #endif
+#ifdef HAVE_HARFBUZZ
+      if (ftfont_info->hb_font)
+       hb_font_destroy (ftfont_info->hb_font);
+#endif
       cache_data->ft_face = NULL;
     }
   else
@@ -1454,6 +1489,24 @@ ftfont_encode_char (struct font *font, int c)
   return (code > 0 ? code : FONT_INVALID_CODE);
 }
 
+static bool
+ftfont_glyph_metrics (FT_Face ft_face, int c, int *advance, int *lbearing,
+                      int *rbearing, int *ascent, int *descent)
+{
+  if (FT_Load_Glyph (ft_face, c, FT_LOAD_DEFAULT) == 0)
+    {
+      FT_Glyph_Metrics *m = &ft_face->glyph->metrics;
+      *advance = m->horiAdvance >> 6;
+      *lbearing = m->horiBearingX >> 6;
+      *rbearing = (m->horiBearingX + m->width) >> 6;
+      *ascent = m->horiBearingY >> 6;
+      *descent = (m->height - m->horiBearingY) >> 6;
+      return true;
+    }
+
+  return false;
+}
+
 void
 ftfont_text_extents (struct font *font, const unsigned int *code,
                     int nglyphs, struct font_metrics *metrics)
@@ -1468,29 +1521,27 @@ ftfont_text_extents (struct font *font, const unsigned 
int *code,
 
   for (i = 0, first = 1; i < nglyphs; i++)
     {
-      if (FT_Load_Glyph (ft_face, code[i], FT_LOAD_DEFAULT) == 0)
+      int advance, lbearing, rbearing, ascent, descent;
+      if (ftfont_glyph_metrics (ft_face, code[i], &advance, &lbearing,
+                                &rbearing, &ascent, &descent))
        {
-         FT_Glyph_Metrics *m = &ft_face->glyph->metrics;
-
          if (first)
            {
-             metrics->lbearing = m->horiBearingX >> 6;
-             metrics->rbearing = (m->horiBearingX + m->width) >> 6;
-             metrics->ascent = m->horiBearingY >> 6;
-             metrics->descent = (m->height - m->horiBearingY) >> 6;
+             metrics->lbearing = lbearing;
+             metrics->rbearing = rbearing;
+             metrics->ascent = ascent;
+             metrics->descent = descent;
              first = 0;
            }
-         if (metrics->lbearing > width + (m->horiBearingX >> 6))
-           metrics->lbearing = width + (m->horiBearingX >> 6);
-         if (metrics->rbearing
-             < width + ((m->horiBearingX + m->width) >> 6))
-           metrics->rbearing
-             = width + ((m->horiBearingX + m->width) >> 6);
-         if (metrics->ascent < (m->horiBearingY >> 6))
-           metrics->ascent = m->horiBearingY >> 6;
-         if (metrics->descent > ((m->height - m->horiBearingY) >> 6))
-           metrics->descent = (m->height - m->horiBearingY) >> 6;
-         width += m->horiAdvance >> 6;
+         if (metrics->lbearing > width + lbearing)
+           metrics->lbearing = width + lbearing;
+         if (metrics->rbearing < width + rbearing)
+           metrics->rbearing = width + rbearing;
+         if (metrics->ascent < ascent)
+           metrics->ascent = ascent;
+         if (metrics->descent > descent)
+           metrics->descent = descent;
+         width += advance;
        }
       else
        width += font->space_width;
@@ -2757,7 +2808,7 @@ ftfont_shape_by_flt (Lisp_Object lgstring, struct font 
*font,
 }
 
 Lisp_Object
-ftfont_shape (Lisp_Object lgstring)
+ftfont_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;
@@ -2769,21 +2820,57 @@ ftfont_shape (Lisp_Object lgstring)
 
 #endif /* HAVE_M17N_FLT */
 
-#ifdef HAVE_OTF_GET_VARIATION_GLYPHS
+#endif /* HAVE_LIBOTF */
 
+#if defined HAVE_OTF_GET_VARIATION_GLYPHS || defined 
HAVE_FT_FACE_GETCHARVARIANTINDEX
 int
 ftfont_variation_glyphs (struct font *font, int c, unsigned variations[256])
 {
   struct font_info *ftfont_info = (struct font_info *) font;
+#ifdef HAVE_OTF_GET_VARIATION_GLYPHS
   OTF *otf = ftfont_get_otf (ftfont_info);
 
   if (! otf)
     return 0;
   return OTF_get_variation_glyphs (otf, c, variations);
+#else  /* !HAVE_OTF_GET_VARIATION_GLYPHS */
+  FT_Face ft_face = ftfont_info->ft_size->face;
+  int i, n = 0;
+
+  for (i = 0; i < 16; i++)
+    {
+      variations[i] = FT_Face_GetCharVariantIndex (ft_face, c, 0xFE00 + i);
+      if (variations[i])
+       n++;
+    }
+  for (; i < 256; i++)
+    {
+      variations[i] = FT_Face_GetCharVariantIndex (ft_face, c,
+                                                  0xE0100 + (i - 16));
+      if (variations[i])
+       n++;
+    }
+
+  return n;
+#endif  /* !HAVE_OTF_GET_VARIATION_GLYPHS */
 }
+#endif /* HAVE_OTF_GET_VARIATION_GLYPHS || HAVE_FT_FACE_GETCHARVARIANTINDEX */
 
-#endif /* HAVE_OTF_GET_VARIATION_GLYPHS */
-#endif /* HAVE_LIBOTF */
+#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;
+}
+
+#endif /* HAVE_HARFBUZZ */
 
 static const char *const ftfont_booleans [] = {
   ":antialias",
@@ -2869,7 +2956,7 @@ static struct font_driver const ftfont_driver =
 #if defined HAVE_M17N_FLT && defined HAVE_LIBOTF
   .shape = ftfont_shape,
 #endif
-#ifdef HAVE_OTF_GET_VARIATION_GLYPHS
+#if defined HAVE_OTF_GET_VARIATION_GLYPHS || defined 
HAVE_FT_FACE_GETCHARVARIANTINDEX
   .get_variation_glyphs = ftfont_variation_glyphs,
 #endif
   .filter_properties = ftfont_filter_properties,
@@ -2881,6 +2968,9 @@ 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");
@@ -2918,4 +3008,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 = hbfont_shape;
+  fthbfont_driver.combining_capability = hbfont_combining_capability;
+  fthbfont_driver.begin_hb_font = fthbfont_begin_hb_font;
+  register_font_driver (&fthbfont_driver, NULL);
+#endif /* HAVE_HARFBUZZ */
 }
diff --git a/src/ftfont.h b/src/ftfont.h
index 7860469..b2280e9 100644
--- a/src/ftfont.h
+++ b/src/ftfont.h
@@ -29,6 +29,11 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 # include FT_BDF_H
 #endif
 
+#ifdef HAVE_HARFBUZZ
+#include <hb.h>
+#include <hb-ft.h>
+#endif  /* HAVE_HARFBUZZ */
+
 #ifdef HAVE_LIBOTF
 # include <otf.h>
 #ifdef HAVE_M17N_FLT
@@ -54,6 +59,9 @@ struct font_info
   FT_Size ft_size;
   int index;
   FT_Matrix matrix;
+#ifdef HAVE_HARFBUZZ
+  hb_font_t *hb_font;
+#endif  /* HAVE_HARFBUZZ */
 
 #ifdef USE_CAIRO
   cairo_scaled_font_t *cr_scaled_font;
diff --git a/src/ftxfont.c b/src/ftxfont.c
index 8bce7f1..b146736 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
@@ -359,7 +351,7 @@ struct font_driver const ftxfont_driver =
 #if defined HAVE_M17N_FLT && defined HAVE_LIBOTF
   .shape = ftfont_shape,
 #endif
-#ifdef HAVE_OTF_GET_VARIATION_GLYPHS
+#if defined HAVE_OTF_GET_VARIATION_GLYPHS || defined 
HAVE_FT_FACE_GETCHARVARIANTINDEX
   .get_variation_glyphs = ftfont_variation_glyphs,
 #endif
   .filter_properties = ftfont_filter_properties,
diff --git a/src/hbfont.c b/src/hbfont.c
new file mode 100644
index 0000000..5923efd
--- /dev/null
+++ b/src/hbfont.c
@@ -0,0 +1,482 @@
+/* hbfont.c -- Platform-independent support for HarfBuzz font driver.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+#include <math.h>
+#include <hb.h>
+
+#include "lisp.h"
+#include "frame.h"
+#include "composite.h"
+#include "font.h"
+#include "dispextern.h"
+
+#ifdef HAVE_NTGUI
+
+#include "w32common.h"
+
+/* The w32 implementation calls HarfBuzz functions via function
+   pointers.  We use the below to declare the function pointers and
+   redirect function names to those pointers.  */
+DEF_DLL_FN (hb_unicode_funcs_t *, hb_unicode_funcs_create,
+           (hb_unicode_funcs_t *));
+DEF_DLL_FN (hb_unicode_funcs_t *, hb_unicode_funcs_get_default, (void));
+DEF_DLL_FN (void, hb_unicode_funcs_set_combining_class_func,
+           (hb_unicode_funcs_t *, hb_unicode_combining_class_func_t,
+            void *, hb_destroy_func_t));
+DEF_DLL_FN (void, hb_unicode_funcs_set_general_category_func,
+           (hb_unicode_funcs_t *, hb_unicode_general_category_func_t,
+            void *, hb_destroy_func_t));
+DEF_DLL_FN (void, hb_unicode_funcs_set_mirroring_func,
+           (hb_unicode_funcs_t *, hb_unicode_mirroring_func_t,
+            void *, hb_destroy_func_t));
+DEF_DLL_FN (hb_buffer_t *, hb_buffer_create, (void));
+DEF_DLL_FN (void, hb_buffer_set_unicode_funcs,
+           (hb_buffer_t *, hb_unicode_funcs_t *));
+DEF_DLL_FN (void, hb_buffer_clear_contents, (hb_buffer_t *));
+DEF_DLL_FN (hb_bool_t, hb_buffer_pre_allocate, (hb_buffer_t *, unsigned int));
+DEF_DLL_FN (void, hb_buffer_add, (hb_buffer_t *, hb_codepoint_t, unsigned 
int));
+DEF_DLL_FN (void, hb_buffer_set_content_type,
+           (hb_buffer_t *, hb_buffer_content_type_t));
+DEF_DLL_FN (void, hb_buffer_set_cluster_level,
+           (hb_buffer_t *, hb_buffer_cluster_level_t));
+DEF_DLL_FN (void, hb_buffer_set_direction, (hb_buffer_t *, hb_direction_t));
+DEF_DLL_FN (void, hb_buffer_set_language, (hb_buffer_t *, hb_language_t));
+DEF_DLL_FN (hb_language_t, hb_language_from_string, (const char *, int));
+DEF_DLL_FN (void, hb_buffer_guess_segment_properties, (hb_buffer_t *));
+DEF_DLL_FN (hb_bool_t, hb_shape_full,
+           (hb_font_t *, hb_buffer_t *, const hb_feature_t *,
+            unsigned int, const char * const *));
+DEF_DLL_FN (unsigned int, hb_buffer_get_length, (hb_buffer_t *));
+DEF_DLL_FN (hb_direction_t, hb_buffer_get_direction, (hb_buffer_t *));
+DEF_DLL_FN (void, hb_buffer_reverse_clusters, (hb_buffer_t *));
+DEF_DLL_FN (hb_glyph_info_t *, hb_buffer_get_glyph_infos,
+           (hb_buffer_t *, unsigned int *));
+DEF_DLL_FN (hb_glyph_position_t *, hb_buffer_get_glyph_positions,
+           (hb_buffer_t *, unsigned int *));
+
+#define hb_unicode_funcs_create fn_hb_unicode_funcs_create
+#define hb_unicode_funcs_get_default fn_hb_unicode_funcs_get_default
+#define hb_unicode_funcs_set_combining_class_func 
fn_hb_unicode_funcs_set_combining_class_func
+#define hb_unicode_funcs_set_general_category_func 
fn_hb_unicode_funcs_set_general_category_func
+#define hb_unicode_funcs_set_mirroring_func 
fn_hb_unicode_funcs_set_mirroring_func
+#define hb_buffer_create fn_hb_buffer_create
+#define hb_buffer_set_unicode_funcs fn_hb_buffer_set_unicode_funcs
+#define hb_buffer_clear_contents fn_hb_buffer_clear_contents
+#define hb_buffer_pre_allocate fn_hb_buffer_pre_allocate
+#define hb_buffer_add fn_hb_buffer_add
+#define hb_buffer_set_content_type fn_hb_buffer_set_content_type
+#define hb_buffer_set_cluster_level fn_hb_buffer_set_cluster_level
+#define hb_buffer_set_direction fn_hb_buffer_set_direction
+#define hb_buffer_set_language fn_hb_buffer_set_language
+#define hb_language_from_string fn_hb_language_from_string
+#define hb_buffer_guess_segment_properties 
fn_hb_buffer_guess_segment_properties
+#define hb_shape_full fn_hb_shape_full
+#define hb_buffer_get_length fn_hb_buffer_get_length
+#define hb_buffer_get_direction fn_hb_buffer_get_direction
+#define hb_buffer_reverse_clusters fn_hb_buffer_reverse_clusters
+#define hb_buffer_get_glyph_infos fn_hb_buffer_get_glyph_infos
+#define hb_buffer_get_glyph_positions fn_hb_buffer_get_glyph_positions
+
+/* This function is called from syms_of_w32uniscribe_for_pdumper to
+   initialize the above function pointers.  */
+bool
+hbfont_init_w32_funcs (HMODULE library)
+{
+  LOAD_DLL_FN (library, hb_unicode_funcs_create);
+  LOAD_DLL_FN (library, hb_unicode_funcs_get_default);
+  LOAD_DLL_FN (library, hb_unicode_funcs_set_combining_class_func);
+  LOAD_DLL_FN (library, hb_unicode_funcs_set_general_category_func);
+  LOAD_DLL_FN (library, hb_unicode_funcs_set_mirroring_func);
+  LOAD_DLL_FN (library, hb_buffer_create);
+  LOAD_DLL_FN (library, hb_buffer_set_unicode_funcs);
+  LOAD_DLL_FN (library, hb_buffer_clear_contents);
+  LOAD_DLL_FN (library, hb_buffer_pre_allocate);
+  LOAD_DLL_FN (library, hb_buffer_add);
+  LOAD_DLL_FN (library, hb_buffer_set_content_type);
+  LOAD_DLL_FN (library, hb_buffer_set_cluster_level);
+  LOAD_DLL_FN (library, hb_buffer_set_direction);
+  LOAD_DLL_FN (library, hb_buffer_set_language);
+  LOAD_DLL_FN (library, hb_language_from_string);
+  LOAD_DLL_FN (library, hb_buffer_guess_segment_properties);
+  LOAD_DLL_FN (library, hb_shape_full);
+  LOAD_DLL_FN (library, hb_buffer_get_length);
+  LOAD_DLL_FN (library, hb_buffer_get_direction);
+  LOAD_DLL_FN (library, hb_buffer_reverse_clusters);
+  LOAD_DLL_FN (library, hb_buffer_get_glyph_infos);
+  LOAD_DLL_FN (library, hb_buffer_get_glyph_positions);
+  return true;
+}
+#endif /* HAVE_NTGUI */
+
+/* Support functions for HarfBuzz shaper.  */
+
+static bool combining_class_loaded = false;
+static Lisp_Object canonical_combining_class_table;
+
+static hb_unicode_combining_class_t
+uni_combining (hb_unicode_funcs_t *funcs, hb_codepoint_t ch, void *user_data)
+{
+  /* Load the Unicode table first time it is needed.  */
+  if (!combining_class_loaded)
+    {
+      canonical_combining_class_table =
+       uniprop_table (intern ("canonical-combining-class"));
+      if (NILP (canonical_combining_class_table))
+       emacs_abort ();
+      staticpro (&canonical_combining_class_table);
+      combining_class_loaded = true;
+    }
+
+  Lisp_Object combining =
+    get_unicode_property (canonical_combining_class_table, ch);
+  if (FIXNUMP (combining))
+    return (hb_unicode_combining_class_t) XFIXNUM (combining);
+
+  return HB_UNICODE_COMBINING_CLASS_NOT_REORDERED;
+}
+
+static hb_unicode_general_category_t
+uni_general (hb_unicode_funcs_t *funcs, hb_codepoint_t ch, void *user_data)
+{
+  Lisp_Object category = CHAR_TABLE_REF (Vunicode_category_table, ch);
+
+  if (INTEGERP (category))
+    {
+    switch (XFIXNUM (category))
+      {
+      case UNICODE_CATEGORY_Cc:
+        return HB_UNICODE_GENERAL_CATEGORY_CONTROL;
+      case UNICODE_CATEGORY_Cf:
+        return HB_UNICODE_GENERAL_CATEGORY_FORMAT;
+      case UNICODE_CATEGORY_Cn:
+        return HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED;
+      case UNICODE_CATEGORY_Co:
+        return HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE;
+      case UNICODE_CATEGORY_Cs:
+        return HB_UNICODE_GENERAL_CATEGORY_SURROGATE;
+      case UNICODE_CATEGORY_Ll:
+        return HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER;
+      case UNICODE_CATEGORY_Lm:
+        return HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER;
+      case UNICODE_CATEGORY_Lo:
+        return HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER;
+      case UNICODE_CATEGORY_Lt:
+        return HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER;
+      case UNICODE_CATEGORY_Lu:
+        return HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER;
+      case UNICODE_CATEGORY_Mc:
+        return HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK;
+      case UNICODE_CATEGORY_Me:
+        return HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK;
+      case UNICODE_CATEGORY_Mn:
+        return HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK;
+      case UNICODE_CATEGORY_Nd:
+        return HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER;
+      case UNICODE_CATEGORY_Nl:
+        return HB_UNICODE_GENERAL_CATEGORY_LETTER_NUMBER;
+      case UNICODE_CATEGORY_No:
+        return HB_UNICODE_GENERAL_CATEGORY_OTHER_NUMBER;
+      case UNICODE_CATEGORY_Pc:
+        return HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION;
+      case UNICODE_CATEGORY_Pd:
+        return HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION;
+      case UNICODE_CATEGORY_Pe:
+        return HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION;
+      case UNICODE_CATEGORY_Pf:
+        return HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION;
+      case UNICODE_CATEGORY_Pi:
+        return HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION;
+      case UNICODE_CATEGORY_Po:
+        return HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION;
+      case UNICODE_CATEGORY_Ps:
+        return HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION;
+      case UNICODE_CATEGORY_Sc:
+        return HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL;
+      case UNICODE_CATEGORY_Sk:
+        return HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL;
+      case UNICODE_CATEGORY_Sm:
+        return HB_UNICODE_GENERAL_CATEGORY_MATH_SYMBOL;
+      case UNICODE_CATEGORY_So:
+        return HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL;
+      case UNICODE_CATEGORY_Zl:
+        return HB_UNICODE_GENERAL_CATEGORY_LINE_SEPARATOR;
+      case UNICODE_CATEGORY_Zp:
+        return HB_UNICODE_GENERAL_CATEGORY_PARAGRAPH_SEPARATOR;
+      case UNICODE_CATEGORY_Zs:
+        return HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR;
+      case UNICODE_CATEGORY_UNKNOWN:
+        return HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED;
+      }
+    }
+
+  return HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED;
+}
+
+static hb_codepoint_t
+uni_mirroring (hb_unicode_funcs_t *funcs, hb_codepoint_t ch, void *user_data)
+{
+  return bidi_mirror_char (ch);
+}
+
+static hb_unicode_funcs_t *
+get_hb_unicode_funcs (void)
+{
+  /* Subclass HarfBuzz's default Unicode functions and override functions that
+   * use data Emacs can provide. This way changing Emacs data is reflected in
+   * the shaped output. */
+  hb_unicode_funcs_t *funcs = hb_unicode_funcs_create 
(hb_unicode_funcs_get_default ());
+
+  hb_unicode_funcs_set_combining_class_func (funcs, uni_combining, NULL, NULL);
+  hb_unicode_funcs_set_general_category_func (funcs, uni_general, NULL, NULL);
+  hb_unicode_funcs_set_mirroring_func (funcs, uni_mirroring, NULL, NULL);
+
+  /* Use default implmentation for Unicode composition/decomposition, we might
+   * want to revisit this later.
+  hb_unicode_funcs_set_compose_func (funcs, uni_compose, NULL, NULL);
+  hb_unicode_funcs_set_decompose_func (funcs, uni_decompose, NULL, NULL);
+  */
+
+  /* Emacs own script mapping for characters differs from Unicode, so we want
+   * to keep the default HarfBuzz's implementation here.
+  hb_unicode_funcs_set_script_func (funcs, uni_script, NULL, NULL);
+  */
+
+  return funcs;
+}
+
+/* HarfBuzz implementation of shape for font backend.
+
+   Shape text in LGSTRING.  See the docstring of
+   'composition-get-gstring' for the format of LGSTRING.  If the
+   (N+1)th element of LGSTRING is nil, input of shaping is from the
+   1st to (N)th elements.  In each input glyph, FROM, TO, CHAR, and
+   CODE are already set, but FROM and TO need adjustments according
+   to the glyphs produced by the shaping fuinction.
+   DIRECTION is either L2R or R2L, or nil if unknown.  During
+   redisplay, this comes from applying the UBA, is passed from
+   composition_reseat_it, and is used by the HarfBuzz shaper.
+
+   This function updates all fields of the input glyphs.  If the
+   output glyphs (M) are more than the input glyphs (N), (N+1)th
+   through (M)th elements of LGSTRING are updated possibly by making
+   a new glyph object and storing it in LGSTRING.  If (M) is greater
+   than the length of LGSTRING, nil should be returned.  In that case,
+   this function is called again with a larger LGSTRING.  */
+Lisp_Object
+hbfont_shape (Lisp_Object lgstring, Lisp_Object direction)
+{
+  struct font *font = CHECK_FONT_GET_OBJECT (LGSTRING_FONT (lgstring));
+  ptrdiff_t glyph_len = 0, text_len = LGSTRING_GLYPH_LEN (lgstring);
+  ptrdiff_t i;
+
+  hb_glyph_info_t *info;
+  hb_glyph_position_t *pos;
+
+  /* Cache the HarfBuzz buffer for better performance and less allocations.
+   * We intentionally never destroy the buffer. */
+  static hb_buffer_t *hb_buffer = NULL;
+  if (! hb_buffer)
+    {
+      hb_buffer = hb_buffer_create ();
+      hb_unicode_funcs_t* ufuncs = get_hb_unicode_funcs();
+      hb_buffer_set_unicode_funcs(hb_buffer, ufuncs);
+    }
+
+  hb_buffer_clear_contents (hb_buffer);
+  hb_buffer_pre_allocate (hb_buffer, text_len);
+
+  /* Copy the characters in their original logical order, so we can
+     assign them to glyphs correctly after shaping.  */
+  int *chars = alloca (text_len * sizeof (int));
+  for (i = 0; i < text_len; i++)
+    {
+      Lisp_Object g = LGSTRING_GLYPH (lgstring, i);
+      int c;
+
+      if (NILP (g))
+       break;
+      c = LGLYPH_CHAR (g);
+      hb_buffer_add (hb_buffer, c, i);
+      chars[i] = c;
+    }
+
+  text_len = i;
+  if (!text_len)
+    return Qnil;
+
+  hb_buffer_set_content_type (hb_buffer, HB_BUFFER_CONTENT_TYPE_UNICODE);
+  hb_buffer_set_cluster_level (hb_buffer,
+                              HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES);
+
+  /* If the caller didn't provide a meaningful DIRECTION, let HarfBuzz
+     guess it. */
+  if (!NILP (direction))
+    {
+      hb_direction_t dir = HB_DIRECTION_LTR;
+      if (EQ (direction, QL2R))
+       dir = HB_DIRECTION_LTR;
+      else if (EQ (direction, QR2L))
+       dir = HB_DIRECTION_RTL;
+      hb_buffer_set_direction (hb_buffer, dir);
+    }
+
+  /* Leave the script determination to HarfBuzz, until Emacs has a
+     better idea of the script of LGSTRING.  FIXME. */
+#if 0
+  hb_buffer_set_script (hb_buffer, XXX);
+#endif
+
+  /* FIXME: This can only handle the single global language, which
+     normally comes from the locale.  In addition, if
+     current-iso639-language is a list, we arbitrarily use the first
+     one.  We should instead have a notion of the language of the text
+     being shaped.  */
+  Lisp_Object lang = Vcurrent_iso639_language;
+  if (CONSP (Vcurrent_iso639_language))
+    lang = XCAR (Vcurrent_iso639_language);
+  if (SYMBOLP (lang))
+    {
+      Lisp_Object lang_str = SYMBOL_NAME (lang);
+      hb_buffer_set_language (hb_buffer,
+                             hb_language_from_string (SSDATA (lang_str),
+                                                      SBYTES (lang_str)));
+    }
+
+  /* Guess the default properties for when they cannot be determined above.
+
+     FIXME: maybe drop this guessing once script and language handling
+     is fixed above; but then will need to guess the direction by
+     ourselves, perhaps by looking at the characters using
+     bidi_get_type or somesuch.  */
+  hb_buffer_guess_segment_properties (hb_buffer);
+
+  double position_unit;
+  hb_font_t *hb_font
+    = font->driver->begin_hb_font
+    ? font->driver->begin_hb_font (font, &position_unit)
+    : NULL;
+  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);
+  if (glyph_len > LGSTRING_GLYPH_LEN (lgstring))
+    return Qnil;
+
+  /* We need the clusters in logical order.  */
+  bool buf_reversed = false;
+  if (HB_DIRECTION_IS_BACKWARD (hb_buffer_get_direction (hb_buffer)))
+    {
+      buf_reversed = true;
+      hb_buffer_reverse_clusters (hb_buffer);
+    }
+  info = hb_buffer_get_glyph_infos (hb_buffer, NULL);
+  pos = hb_buffer_get_glyph_positions (hb_buffer, NULL);
+  ptrdiff_t from = -1, to UNINIT, cluster_offset UNINIT;
+  int incr = buf_reversed ? -1 : 1;
+  for (i = 0; i < glyph_len; i++)
+    {
+      Lisp_Object lglyph = LGSTRING_GLYPH (lgstring, i);
+      struct font_metrics metrics = {.width = 0};
+      int xoff, yoff, wadjust;
+
+      if (NILP (lglyph))
+       {
+         lglyph = LGLYPH_NEW ();
+         LGSTRING_SET_GLYPH (lgstring, i, lglyph);
+       }
+
+      if (info[i].cluster != from)
+       {
+         int j;
+         /* Found a new cluster.  Determine its FROM and TO, and the
+            offset to the first character of the cluster.  */
+         /* FROM is the index of the first character that contributed
+            to this cluster.  */
+         from = info[i].cluster;
+         /* TO is the index of the last character that contributed to
+            this cluster.  */
+         for (j = i; j < glyph_len && info[j].cluster == from; j++)
+           ;
+         to = (j == glyph_len) ? text_len - 1 : info[j].cluster - 1;
+         cluster_offset = 0;
+         /* For RTL buffers, HarfBuzz produces glyphs in a cluster in
+            reverse order, so we need to account for that to record
+            the correct character in each glyph.
+
+            Implementation note: the character codepoint recorded in
+            each glyph is not really used, except when we display the
+            glyphs in descr-text.el.  So this is just an aeasthetic
+            issue.  */
+         if (buf_reversed)
+           cluster_offset = to - from;
+       }
+
+      /* All the glyphs in a cluster have the same values of FROM and TO.  */
+      LGLYPH_SET_FROM (lglyph, from);
+      LGLYPH_SET_TO (lglyph, to);
+
+      /* Not every glyph in a cluster maps directly to a single
+        character; in general, N characters can yield M glyphs, where
+        M could be smaller or greater than N.  However, in many cases
+        there is a one-to-one correspondence, and it would be a pity
+        to lose that information, even if it's sometimes inaccurate.  */
+      ptrdiff_t char_idx = from + cluster_offset;
+      cluster_offset += incr;
+      if (char_idx > to)
+       char_idx = to;
+      if (char_idx < from)
+       char_idx = from;
+      LGLYPH_SET_CHAR (lglyph, chars[char_idx]);
+      LGLYPH_SET_CODE (lglyph, info[i].codepoint);
+
+      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);
+}
+
+Lisp_Object
+hbfont_combining_capability (struct font *font)
+{
+  return Qt;
+}
diff --git a/src/indent.c b/src/indent.c
index 90d8b1c..f0d709e 100644
--- a/src/indent.c
+++ b/src/indent.c
@@ -599,7 +599,7 @@ scan_for_column (ptrdiff_t *endpos, EMACS_INT *goalcol, 
ptrdiff_t *prevcol)
       if (cmp_it.id >= 0
          || (scan == cmp_it.stop_pos
              && composition_reseat_it (&cmp_it, scan, scan_byte, end,
-                                       w, NULL, Qnil)))
+                                       w, NEUTRAL_DIR, NULL, Qnil)))
        composition_update_it (&cmp_it, scan, scan_byte, Qnil);
       if (cmp_it.id >= 0)
        {
@@ -1507,7 +1507,7 @@ compute_motion (ptrdiff_t from, ptrdiff_t frombyte, 
EMACS_INT fromvpos,
          if (cmp_it.id >= 0
              || (pos == cmp_it.stop_pos
                  && composition_reseat_it (&cmp_it, pos, pos_byte, to, win,
-                                           NULL, Qnil)))
+                                           NEUTRAL_DIR, NULL, Qnil)))
            composition_update_it (&cmp_it, pos, pos_byte, Qnil);
          if (cmp_it.id >= 0)
            {
diff --git a/src/lisp.h b/src/lisp.h
index 6db9059..5bd88f3 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -3994,6 +3994,7 @@ extern void map_char_table_for_charset (void 
(*c_function) (Lisp_Object, Lisp_Ob
                                        Lisp_Object, struct charset *,
                                        unsigned, unsigned);
 extern Lisp_Object uniprop_table (Lisp_Object);
+extern Lisp_Object get_unicode_property (Lisp_Object, int);
 extern void syms_of_chartab (void);
 
 /* Defined in print.c.  */
diff --git a/src/macfont.m b/src/macfont.m
index f825e62..abdf0ec 100644
--- a/src/macfont.m
+++ b/src/macfont.m
@@ -39,6 +39,12 @@ Original author: YAMAMOTO Mitsuharu
 
 #include <libkern/OSByteOrder.h>
 
+/* Values for `dir' argument to shaper functions.  */
+enum lgstring_direction
+  {
+    DIR_R2L = -1, DIR_UNKNOWN = 0, DIR_L2R = 1,
+  };
+
 static double mac_font_get_advance_width_for_glyph (CTFontRef, CGGlyph);
 static CGRect mac_font_get_bounding_rect_for_glyph (CTFontRef, CGGlyph);
 static CFArrayRef mac_font_create_available_families (void);
@@ -49,7 +55,8 @@ static Boolean mac_font_descriptor_supports_languages 
(CTFontDescriptorRef,
                                                       CFArrayRef);
 static CFStringRef mac_font_create_preferred_family_for_attributes 
(CFDictionaryRef);
 static CFIndex mac_font_shape (CTFontRef, CFStringRef,
-                              struct mac_glyph_layout *, CFIndex);
+                              struct mac_glyph_layout *, CFIndex,
+                              enum lgstring_direction);
 static CFArrayRef mac_font_copy_default_descriptors_for_language (CFStringRef);
 static CFStringRef mac_font_copy_default_name_for_charset_and_languages 
(CFCharacterSetRef, CFArrayRef);
 #if USE_CT_GLYPH_INFO
@@ -318,7 +325,8 @@ mac_screen_font_get_metrics (ScreenFontRef font, CGFloat 
*ascent,
 
 static CFIndex
 mac_font_shape_1 (NSFont *font, NSString *string,
-                  struct mac_glyph_layout *glyph_layouts, CFIndex glyph_len)
+                  struct mac_glyph_layout *glyph_layouts, CFIndex glyph_len,
+                 enum lgstring_direction dir)
 {
   NSUInteger i;
   CFIndex result = 0;
@@ -582,11 +590,11 @@ mac_font_shape_1 (NSFont *font, NSString *string,
 static CFIndex
 mac_screen_font_shape (ScreenFontRef font, CFStringRef string,
                        struct mac_glyph_layout *glyph_layouts,
-                       CFIndex glyph_len)
+                       CFIndex glyph_len, enum lgstring_direction dir)
 {
   return mac_font_shape_1 ([(NSFont *)font printerFont],
                            (NSString *) string,
-                           glyph_layouts, glyph_len);
+                           glyph_layouts, glyph_len, dir);
 }
 
 static CGColorRef
@@ -1642,7 +1650,7 @@ static unsigned macfont_encode_char (struct font *, int);
 static void macfont_text_extents (struct font *, const unsigned int *, int,
                                   struct font_metrics *);
 static int macfont_draw (struct glyph_string *, int, int, int, int, bool);
-static Lisp_Object macfont_shape (Lisp_Object);
+static Lisp_Object macfont_shape (Lisp_Object, Lisp_Object);
 static int macfont_variation_glyphs (struct font *, int c,
                                      unsigned variations[256]);
 static void macfont_filter_properties (Lisp_Object, Lisp_Object);
@@ -2926,7 +2934,7 @@ macfont_draw (struct glyph_string *s, int from, int to, 
int x, int y,
 }
 
 static Lisp_Object
-macfont_shape (Lisp_Object lgstring)
+macfont_shape (Lisp_Object lgstring, Lisp_Object direction)
 {
   struct font *font = CHECK_FONT_GET_OBJECT (LGSTRING_FONT (lgstring));
   struct macfont_info *macfont_info = (struct macfont_info *) font;
@@ -2976,12 +2984,18 @@ macfont_shape (Lisp_Object lgstring)
                                                kCFAllocatorNull);
   if (string)
     {
+      enum lgstring_direction dir = DIR_UNKNOWN;
+
+      if (EQ (direction, QL2R))
+       dir = DIR_L2R;
+      else if (EQ (direction, QR2L))
+       dir = DIR_R2L;
       glyph_layouts = alloca (sizeof (struct mac_glyph_layout) * glyph_len);
       if (macfont_info->screen_font)
         used = mac_screen_font_shape (macfont_info->screen_font, string,
-                                      glyph_layouts, glyph_len);
+                                      glyph_layouts, glyph_len, dir);
       else
-        used = mac_font_shape (macfont, string, glyph_layouts, glyph_len);
+        used = mac_font_shape (macfont, string, glyph_layouts, glyph_len, dir);
       CFRelease (string);
     }
 
@@ -3662,7 +3676,8 @@ mac_font_create_line_with_string_and_font (CFStringRef 
string,
 
 static CFIndex
 mac_font_shape (CTFontRef font, CFStringRef string,
-               struct mac_glyph_layout *glyph_layouts, CFIndex glyph_len)
+               struct mac_glyph_layout *glyph_layouts, CFIndex glyph_len,
+               enum lgstring_direction dir)
 {
   CFIndex used, result = 0;
   CTLineRef ctline = mac_font_create_line_with_string_and_font (string, font);
diff --git a/src/w32common.h b/src/w32common.h
index bca5244..adde784 100644
--- a/src/w32common.h
+++ b/src/w32common.h
@@ -81,4 +81,8 @@ get_proc_addr (HINSTANCE handle, LPCSTR fname)
     }                                                                  \
   while (false)
 
+#ifdef HAVE_HARFBUZZ
+extern bool hbfont_init_w32_funcs (HMODULE);
+#endif
+
 #endif /* W32COMMON_H */
diff --git a/src/w32fns.c b/src/w32fns.c
index bb74fcc..8ebfc11 100644
--- a/src/w32fns.c
+++ b/src/w32fns.c
@@ -221,6 +221,7 @@ int menubar_in_use = 0;
 /* From w32uniscribe.c  */
 extern void syms_of_w32uniscribe (void);
 extern int uniscribe_available;
+extern int harfbuzz_available;
 
 #ifdef WINDOWSNT
 /* From w32inevt.c */
@@ -5843,7 +5844,45 @@ DEFUN ("x-create-frame", Fx_create_frame, 
Sx_create_frame,
       specbind (Qx_resource_name, name);
     }
 
-  if (uniscribe_available)
+  bool register_uniscribe = uniscribe_available;
+#ifdef HAVE_HARFBUZZ
+  /* Register Uniscribe only if HarfBuzz is not available or if
+     explicitly requested.  If Uniscribe is registered, register
+     HarfBuzz only if explicitly requested.  */
+  bool register_harfbuzz = harfbuzz_available;
+  if (register_harfbuzz)
+    register_uniscribe = false;
+  Lisp_Object dflt_backends
+    = gui_display_get_arg (dpyinfo, parameters, Qfont_backend,
+                          "fontBackend", "FontBackend", RES_TYPE_STRING);
+  if (!EQ (dflt_backends, Qunbound))
+    {
+      bool harfbuzz_requested = false, uniscribe_requested = false;
+      if (CONSP (dflt_backends))
+       {
+         if (!NILP (Fmemq (Quniscribe, dflt_backends)))
+           uniscribe_requested = true;
+         if (!NILP (Fmemq (Qharfbuzz, dflt_backends)))
+           harfbuzz_requested = true;
+       }
+      else if (STRINGP (dflt_backends))
+       {
+         if (strcmp (SSDATA (dflt_backends), "uniscribe") == 0)
+           uniscribe_requested = true;
+         else if (strcmp (SSDATA (dflt_backends), "harfbuzz") == 0)
+           harfbuzz_requested = true;
+       }
+      if (uniscribe_requested)
+       {
+         register_uniscribe = uniscribe_available;
+         if (!harfbuzz_requested)
+           register_harfbuzz = false;
+       }
+    }
+  if (register_harfbuzz)
+    register_font_driver (&harfbuzz_font_driver, f);
+#endif
+  if (register_uniscribe)
     register_font_driver (&uniscribe_font_driver, f);
   register_font_driver (&w32font_driver, f);
 
@@ -6896,7 +6935,45 @@ w32_create_tip_frame (struct w32_display_info *dpyinfo, 
Lisp_Object parms)
       specbind (Qx_resource_name, name);
     }
 
-  if (uniscribe_available)
+  bool register_uniscribe = uniscribe_available;
+#ifdef HAVE_HARFBUZZ
+  /* Register Uniscribe only if HarfBuzz is not available or if
+     explicitly requested.  If Uniscribe is registered, register
+     HarfBuzz only if explicitly requested.  */
+  bool register_harfbuzz = harfbuzz_available;
+  if (register_harfbuzz)
+    register_uniscribe = false;
+  Lisp_Object dflt_backends
+    = gui_display_get_arg (dpyinfo, parms, Qfont_backend,
+                          "fontBackend", "FontBackend", RES_TYPE_STRING);
+  if (!EQ (dflt_backends, Qunbound))
+    {
+      bool harfbuzz_requested = false, uniscribe_requested = false;
+      if (CONSP (dflt_backends))
+       {
+         if (!NILP (Fmemq (Quniscribe, dflt_backends)))
+           uniscribe_requested = true;
+         if (!NILP (Fmemq (Qharfbuzz, dflt_backends)))
+           harfbuzz_requested = true;
+       }
+      else if (STRINGP (dflt_backends))
+       {
+         if (strcmp (SSDATA (dflt_backends), "uniscribe") == 0)
+           uniscribe_requested = true;
+         else if (strcmp (SSDATA (dflt_backends), "harfbuzz") == 0)
+           harfbuzz_requested = true;
+       }
+      if (uniscribe_requested)
+       {
+         register_uniscribe = uniscribe_available;
+         if (!harfbuzz_requested)
+           register_harfbuzz = false;
+       }
+    }
+  if (register_harfbuzz)
+    register_font_driver (&harfbuzz_font_driver, f);
+#endif
+  if (register_uniscribe)
     register_font_driver (&uniscribe_font_driver, f);
   register_font_driver (&w32font_driver, f);
 
diff --git a/src/w32font.c b/src/w32font.c
index 47a33ae..14d49b2 100644
--- a/src/w32font.c
+++ b/src/w32font.c
@@ -2646,6 +2646,7 @@ syms_of_w32font (void)
 {
   DEFSYM (Qgdi, "gdi");
   DEFSYM (Quniscribe, "uniscribe");
+  DEFSYM (Qharfbuzz, "harfbuzz");
   DEFSYM (QCformat, ":format");
 
   /* Generic font families.  */
diff --git a/src/w32uniscribe.c b/src/w32uniscribe.c
index 72b524f..caf346a 100644
--- a/src/w32uniscribe.c
+++ b/src/w32uniscribe.c
@@ -1,4 +1,5 @@
 /* Font backend for the Microsoft W32 Uniscribe API.
+   Windows-specific parts of the HarfBuzz font backend.
    Copyright (C) 2008-2019 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
@@ -29,6 +30,14 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #define _WIN32_WINNT 0x0600
 #include <windows.h>
 #include <usp10.h>
+#ifdef HAVE_HARFBUZZ
+# include <hb.h>
+# if GNUC_PREREQ (4, 3, 0)
+#  define bswap_32(v)  __builtin_bswap32(v)
+# else
+#  include <byteswap.h>
+# endif
+#endif
 
 #include "lisp.h"
 #include "w32term.h"
@@ -39,10 +48,16 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include "pdumper.h"
 #include "w32common.h"
 
+/* Extension of w32font_info used by Uniscribe and HarfBuzz backends.  */
 struct uniscribe_font_info
 {
   struct w32font_info w32_font;
-  SCRIPT_CACHE cache;
+  /* This is used by the Uniscribe backend as a pointer to the script
+     cache, and by the HarfBuzz backend as a pointer to a hb_font_t
+     object.  */
+  void *cache;
+  /* This is used by the HarfBuzz backend to store the font scale.  */
+  double scale;
 };
 
 int uniscribe_available = 0;
@@ -51,6 +66,39 @@ int uniscribe_available = 0;
 static int CALLBACK ALIGN_STACK add_opentype_font_name_to_list (ENUMLOGFONTEX 
*,
                                                                NEWTEXTMETRICEX 
*,
                                                                DWORD, LPARAM);
+#ifdef HAVE_HARFBUZZ
+
+struct font_driver harfbuzz_font_driver;
+int harfbuzz_available = 0;
+
+/* Typedefs for HarfBuzz functions which we call through function
+   pointers initialized after we load the HarfBuzz DLL.  */
+DEF_DLL_FN (hb_blob_t *, hb_blob_create,
+           (const char *, unsigned int, hb_memory_mode_t, void *,
+            hb_destroy_func_t));
+DEF_DLL_FN (hb_face_t *, hb_face_create_for_tables,
+           (hb_reference_table_func_t, void *, hb_destroy_func_t));
+DEF_DLL_FN (unsigned, hb_face_get_glyph_count, (const hb_face_t *));
+DEF_DLL_FN (hb_font_t *, hb_font_create, (hb_face_t *));
+DEF_DLL_FN (void, hb_font_destroy, (hb_font_t *));
+DEF_DLL_FN (void, hb_face_destroy, (hb_face_t *));
+DEF_DLL_FN (unsigned int, hb_face_get_upem, (hb_face_t *));
+DEF_DLL_FN (hb_bool_t, hb_font_get_nominal_glyph,
+           (hb_font_t *, hb_codepoint_t, hb_codepoint_t *));
+DEF_DLL_FN (hb_bool_t, hb_font_get_variation_glyph,
+           (hb_font_t *, hb_codepoint_t, hb_codepoint_t, hb_codepoint_t *));
+
+#define hb_blob_create fn_hb_blob_create
+#define hb_face_create_for_tables fn_hb_face_create_for_tables
+#define hb_face_get_glyph_count fn_hb_face_get_glyph_count
+#define hb_font_create fn_hb_font_create
+#define hb_font_destroy fn_hb_font_destroy
+#define hb_face_destroy fn_hb_face_destroy
+#define hb_face_get_upem fn_hb_face_get_upem
+#define hb_font_get_nominal_glyph fn_hb_font_get_nominal_glyph
+#define hb_font_get_variation_glyph fn_hb_font_get_variation_glyph
+#endif
+
 /* Used by uniscribe_otf_capability.  */
 static Lisp_Object otf_features (HDC context, const char *table);
 
@@ -117,7 +165,10 @@ uniscribe_open (struct frame *f, Lisp_Object font_entity, 
int pixel_size)
   struct uniscribe_font_info *uniscribe_font
     = (struct uniscribe_font_info *) XFONT_OBJECT (font_object);
 
-  ASET (font_object, FONT_TYPE_INDEX, Quniscribe);
+  if (!NILP (AREF (font_entity, FONT_TYPE_INDEX)))
+    ASET (font_object, FONT_TYPE_INDEX, AREF (font_entity, FONT_TYPE_INDEX));
+  else /* paranoia: this should never happen */
+    ASET (font_object, FONT_TYPE_INDEX, Quniscribe);
 
   if (!w32font_open_internal (f, font_entity, pixel_size, font_object))
     {
@@ -127,10 +178,15 @@ uniscribe_open (struct frame *f, Lisp_Object font_entity, 
int pixel_size)
   /* Initialize the cache for this font.  */
   uniscribe_font->cache = NULL;
 
-  /* Uniscribe backend uses glyph indices.  */
+  /* Uniscribe and HarfBuzz backends use glyph indices.  */
   uniscribe_font->w32_font.glyph_idx = ETO_GLYPH_INDEX;
 
-  uniscribe_font->w32_font.font.driver = &uniscribe_font_driver;
+#ifdef HAVE_HARFBUZZ
+  if (EQ (AREF (font_object, FONT_TYPE_INDEX), Qharfbuzz))
+    uniscribe_font->w32_font.font.driver = &harfbuzz_font_driver;
+  else
+#endif  /* HAVE_HARFBUZZ */
+    uniscribe_font->w32_font.font.driver = &uniscribe_font_driver;
 
   return font_object;
 }
@@ -141,8 +197,16 @@ uniscribe_close (struct font *font)
   struct uniscribe_font_info *uniscribe_font
     = (struct uniscribe_font_info *) font;
 
+#ifdef HAVE_HARFBUZZ
+  if (uniscribe_font->w32_font.font.driver == &harfbuzz_font_driver
+      && uniscribe_font->cache)
+    hb_font_destroy ((hb_font_t *) uniscribe_font->cache);
+  else
+#endif
   if (uniscribe_font->cache)
-    ScriptFreeCache (&(uniscribe_font->cache));
+    ScriptFreeCache ((SCRIPT_CACHE) &(uniscribe_font->cache));
+
+  uniscribe_font->cache = NULL;
 
   w32font_close (font);
 }
@@ -205,6 +269,9 @@ uniscribe_otf_capability (struct font *font)
    (N+1)th element of LGSTRING is nil, input of shaping is from the
    1st to (N)th elements.  In each input glyph, FROM, TO, CHAR, and
    CODE are already set.
+   DIRECTION is either L2R or R2L, or nil if unknown.  During
+   redisplay, this comes from applying the UBA, is passed from
+   composition_reseat_it, and is used by the HarfBuzz shaper.
 
    This function updates all fields of the input glyphs.  If the
    output glyphs (M) are more than the input glyphs (N), (N+1)th
@@ -213,7 +280,7 @@ uniscribe_otf_capability (struct font *font)
    than the length of LGSTRING, nil should be returned.  In that case,
    this function is called again with a larger LGSTRING.  */
 static Lisp_Object
-uniscribe_shape (Lisp_Object lgstring)
+uniscribe_shape (Lisp_Object lgstring, Lisp_Object direction)
 {
   struct font *font = CHECK_FONT_GET_OBJECT (LGSTRING_FONT (lgstring));
   struct uniscribe_font_info *uniscribe_font
@@ -287,7 +354,7 @@ uniscribe_shape (Lisp_Object lgstring)
 
       /* Context may be NULL here, in which case the cache should be
          used without needing to select the font.  */
-      result = ScriptShape (context, &(uniscribe_font->cache),
+      result = ScriptShape (context, (SCRIPT_CACHE) &(uniscribe_font->cache),
                            chars + items[i].iCharPos, nchars_in_run,
                            max_glyphs - done_glyphs, &(items[i].a),
                            glyphs, clusters, attributes, &nglyphs);
@@ -301,7 +368,7 @@ uniscribe_shape (Lisp_Object lgstring)
          context = get_frame_dc (f);
          old_font = SelectObject (context, FONT_HANDLE (font));
 
-         result = ScriptShape (context, &(uniscribe_font->cache),
+         result = ScriptShape (context, (SCRIPT_CACHE) 
&(uniscribe_font->cache),
                                chars + items[i].iCharPos, nchars_in_run,
                                max_glyphs - done_glyphs, &(items[i].a),
                                glyphs, clusters, attributes, &nglyphs);
@@ -326,7 +393,7 @@ uniscribe_shape (Lisp_Object lgstring)
        }
       else
        {
-         result = ScriptPlace (context, &(uniscribe_font->cache),
+         result = ScriptPlace (context, (SCRIPT_CACHE) 
&(uniscribe_font->cache),
                                glyphs, nglyphs, attributes, &(items[i].a),
                                advances, offsets, &overall_metrics);
          if (result == E_PENDING && !context)
@@ -336,13 +403,15 @@ uniscribe_shape (Lisp_Object lgstring)
              context = get_frame_dc (f);
              old_font = SelectObject (context, FONT_HANDLE (font));
 
-             result = ScriptPlace (context, &(uniscribe_font->cache),
+             result = ScriptPlace (context,
+                                   (SCRIPT_CACHE) &(uniscribe_font->cache),
                                    glyphs, nglyphs, attributes, &(items[i].a),
                                    advances, offsets, &overall_metrics);
            }
           if (SUCCEEDED (result))
            {
              int j, from, to, adj_offset = 0;
+             int cluster_offset = 0;
 
              from = 0;
              to = from;
@@ -386,6 +455,7 @@ uniscribe_shape (Lisp_Object lgstring)
                                }
                            }
                        }
+                     cluster_offset = 0;
 
                      /* For RTL text, the Uniscribe shaper prepares
                         the values in ADVANCES array for layout in
@@ -401,6 +471,8 @@ uniscribe_shape (Lisp_Object lgstring)
                         adjustment for the base character, which is
                         then updated for each successive glyph in the
                         grapheme cluster.  */
+                     /* FIXME: Should we use DIRECTION here instead
+                        of what ScriptItemize guessed?  */
                      if (items[i].a.fRTL)
                        {
                          int j1 = j;
@@ -414,8 +486,11 @@ uniscribe_shape (Lisp_Object lgstring)
                        }
                    }
 
-                 LGLYPH_SET_CHAR (lglyph, chars[items[i].iCharPos
-                                                + from]);
+                 int char_idx = items[i].iCharPos + from + cluster_offset;
+                 if (from + cluster_offset > to)
+                   char_idx = items[i].iCharPos + to;
+                 cluster_offset++;
+                 LGLYPH_SET_CHAR (lglyph, chars[char_idx]);
                  LGLYPH_SET_FROM (lglyph, items[i].iCharPos + from);
                  LGLYPH_SET_TO (lglyph, items[i].iCharPos + to);
 
@@ -424,18 +499,18 @@ uniscribe_shape (Lisp_Object lgstring)
                  LGLYPH_SET_ASCENT (lglyph, font->ascent);
                  LGLYPH_SET_DESCENT (lglyph, font->descent);
 
-                 result = ScriptGetGlyphABCWidth (context,
-                                                  &(uniscribe_font->cache),
-                                                  glyphs[j], &char_metric);
+                 result = ScriptGetGlyphABCWidth
+                   (context, (SCRIPT_CACHE) &(uniscribe_font->cache),
+                    glyphs[j], &char_metric);
                  if (result == E_PENDING && !context)
                    {
                      /* Cache incomplete... */
                      f = XFRAME (selected_frame);
                      context = get_frame_dc (f);
                      old_font = SelectObject (context, FONT_HANDLE (font));
-                     result = ScriptGetGlyphABCWidth (context,
-                                                      &(uniscribe_font->cache),
-                                                      glyphs[j], &char_metric);
+                     result = ScriptGetGlyphABCWidth
+                       (context, (SCRIPT_CACHE) &(uniscribe_font->cache),
+                        glyphs[j], &char_metric);
                    }
 
                  if (SUCCEEDED (result))
@@ -567,7 +642,8 @@ uniscribe_encode_char (struct font *font, int c)
             order.  */
          items[0].a.fLogicalOrder = 1;
 
-          result = ScriptShape (context, &(uniscribe_font->cache),
+          result = ScriptShape (context,
+                               (SCRIPT_CACHE) &(uniscribe_font->cache),
                                 ch, len, 2, &(items[0].a),
                                 glyphs, clusters, attrs, &nglyphs);
 
@@ -578,7 +654,8 @@ uniscribe_encode_char (struct font *font, int c)
               f = XFRAME (selected_frame);
               context = get_frame_dc (f);
               old_font = SelectObject (context, FONT_HANDLE (font));
-              result = ScriptShape (context, &(uniscribe_font->cache),
+              result = ScriptShape (context,
+                                   (SCRIPT_CACHE) &(uniscribe_font->cache),
                                     ch, len, 2, &(items[0].a),
                                     glyphs, clusters, attrs, &nglyphs);
             }
@@ -596,7 +673,8 @@ uniscribe_encode_char (struct font *font, int c)
                  when shaped. But we still need the return from here
                  to be valid for the shaping engine to be invoked
                  later.  */
-              result = ScriptGetCMap (context, &(uniscribe_font->cache),
+              result = ScriptGetCMap (context,
+                                     (SCRIPT_CACHE) &(uniscribe_font->cache),
                                       ch, len, 0, glyphs);
               if (SUCCEEDED (result) && glyphs[0])
                 code = glyphs[0];
@@ -1143,6 +1221,210 @@ font_table_error:
   return Qnil;
 }
 
+#ifdef HAVE_HARFBUZZ
+
+/* W32 implementation of the 'list' method for HarfBuzz backend.  */
+static Lisp_Object
+w32hb_list (struct frame *f, Lisp_Object font_spec)
+{
+  Lisp_Object fonts = w32font_list_internal (f, font_spec, true);
+  FONT_ADD_LOG ("harfbuzz-list", font_spec, fonts);
+
+  for (Lisp_Object tail = fonts; CONSP (tail); tail = XCDR (tail))
+    ASET (XCAR (tail), FONT_TYPE_INDEX, Qharfbuzz);
+
+  return fonts;
+}
+
+/* W32 implementation of the 'match' method for HarfBuzz backend.  */
+static Lisp_Object
+w32hb_match (struct frame *f, Lisp_Object font_spec)
+{
+  Lisp_Object entity = w32font_match_internal (f, font_spec, true);
+  FONT_ADD_LOG ("harfbuzz-match", font_spec, entity);
+
+  if (! NILP (entity))
+    ASET (entity, FONT_TYPE_INDEX, Qharfbuzz);
+  return entity;
+}
+
+/* Callback function to free memory.  We need this so we could pass it
+   to HarfBuzz as the function to call to destroy objects for which we
+   allocated data by calling our 'malloc' (as opposed to 'malloc' from
+   the MS CRT, against which HarfBuzz was linked).  */
+static void
+free_cb (void *ptr)
+{
+  free (ptr);
+}
+
+/* A function used as reference_table_func for HarfBuzz.  It returns
+   the data of a specified table of a font as a blob.  */
+static hb_blob_t *
+w32hb_get_font_table (hb_face_t *face, hb_tag_t tag, void *data)
+{
+  struct frame *f = XFRAME (selected_frame);
+  HDC context = get_frame_dc (f);
+  HFONT old_font = SelectObject (context, (HFONT) data);
+  char *font_data = NULL;
+  DWORD font_data_size = 0, val;
+  DWORD table = bswap_32 (tag);
+  hb_blob_t *blob = NULL;
+
+  val = GetFontData (context, table, 0, font_data, font_data_size);
+  if (val != GDI_ERROR)
+    {
+      font_data_size = val;
+      /* Don't call xmalloc, because it can signal an error, while
+        we are inside a critical section established by get_frame_dc.  */
+      font_data = malloc (font_data_size);
+      if (font_data)
+       {
+         val = GetFontData (context, table, 0, font_data, font_data_size);
+         if (val != GDI_ERROR)
+           blob = hb_blob_create (font_data, font_data_size,
+                                  HB_MEMORY_MODE_READONLY, font_data, free_cb);
+       }
+    }
+
+  /* Restore graphics context.  */
+  SelectObject (context, old_font);
+  release_frame_dc (f, context);
+
+  return blob;
+}
+
+/* Helper function used by the HarfBuzz implementations of the
+   encode_char, has_char, and begin_hb_font methods.  It creates an
+   hb_font_t object for a given Emacs font.  */
+static hb_font_t *
+w32hb_get_font (struct font *font, double *scale)
+{
+  hb_font_t *hb_font = NULL;
+  HFONT font_handle = FONT_HANDLE (font);
+  hb_face_t *hb_face =
+    hb_face_create_for_tables (w32hb_get_font_table, font_handle, NULL);
+  if (hb_face_get_glyph_count (hb_face) > 0)
+    hb_font = hb_font_create (hb_face);
+
+  struct uniscribe_font_info *uniscribe_font =
+    (struct uniscribe_font_info *) font;
+  unsigned upem = hb_face_get_upem (hb_face);
+  eassert (upem > 0);
+  /* 
https://support.microsoft.com/en-sg/help/74299/info-calculating-the-logical-height-and-point-size-of-a-font.
  */
+  LONG font_point_size =
+    uniscribe_font->w32_font.metrics.tmHeight
+    - uniscribe_font->w32_font.metrics.tmInternalLeading;
+  /* https://docs.microsoft.com/en-us/typography/opentype/spec/ttch01,
+     under "Converting FUnits to pixels".  */
+  *scale = font_point_size * 1.0 / upem;
+
+  hb_face_destroy (hb_face);
+
+  /* FIXME: Can hb_font be non-NULL and yet invalid?  Compare to get_empty?  */
+  return hb_font;
+}
+
+/* W32 implementation of encode_char method for HarfBuzz backend.  */
+static unsigned
+w32hb_encode_char (struct font *font, int c)
+{
+  struct uniscribe_font_info *uniscribe_font
+    = (struct uniscribe_font_info *) font;
+  eassert (uniscribe_font->w32_font.font.driver == &harfbuzz_font_driver);
+  hb_font_t *hb_font = uniscribe_font->cache;
+
+  /* First time we use this font with HarfBuzz, create the hb_font_t
+     object and cache it.  */
+  if (!hb_font)
+    {
+      double scale;
+      hb_font = w32hb_get_font (font, &scale);
+      if (!hb_font)
+       return FONT_INVALID_CODE;
+
+      uniscribe_font->cache = hb_font;
+      eassert (scale > 0.0);
+      uniscribe_font->scale = scale;
+    }
+  hb_codepoint_t glyph;
+  if (hb_font_get_nominal_glyph (hb_font, c, &glyph))
+    return glyph;
+  return FONT_INVALID_CODE;
+}
+
+/* W32 implementation of HarfBuzz begin_hb_font and end_hb_font
+   methods.  */
+
+/* Return a HarfBuzz font object for FONT and store in 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.  */
+static hb_font_t *
+w32hb_begin_font (struct font *font, double *position_unit)
+{
+  struct uniscribe_font_info *uniscribe_font
+    = (struct uniscribe_font_info *) font;
+  eassert (uniscribe_font->w32_font.font.driver == &harfbuzz_font_driver);
+
+  /* First time we use this font with HarfBuzz, create the hb_font_t
+     object and cache it.  */
+  if (!uniscribe_font->cache)
+    {
+      double scale;
+      uniscribe_font->cache = w32hb_get_font (font, &scale);
+      eassert (scale > 0.0);
+      uniscribe_font->scale = scale;
+    }
+  *position_unit = uniscribe_font->scale;
+  return (hb_font_t *) uniscribe_font->cache;
+}
+
+/* W32 implementation of get_variation_glyphs method for HarfBuzz.
+
+   Return the number of variation glyphs of character C supported by
+   FONT.  VARIATIONS is an array of 256 elements.  If the variation
+   selector N (1..256) defines a glyph, that glyph code is stored in
+   the (N-1)th element of VARIATIONS.  */
+static int
+w32hb_get_variation_glyphs (struct font *font, int c, unsigned variations[256])
+{
+  struct uniscribe_font_info *uniscribe_font
+    = (struct uniscribe_font_info *) font;
+  eassert (uniscribe_font->w32_font.font.driver == &harfbuzz_font_driver);
+
+  /* First time we use this font with HarfBuzz, create the hb_font_t
+     object and cache it.  */
+  if (!uniscribe_font->cache)
+    {
+      double scale;
+      uniscribe_font->cache = w32hb_get_font (font, &scale);
+      eassert (scale > 0.0);
+      uniscribe_font->scale = scale;
+    }
+
+  int i, n = 0;
+  hb_font_t *hb_font = uniscribe_font->cache;
+  for (i = 0; i < 16; i++)
+    {
+      if (hb_font_get_variation_glyph (hb_font, c, 0xFE00 + i, &variations[i]))
+       n++;
+      else
+       variations[i] = 0;
+    }
+  for ( ; i < 256; i++)
+    {
+      if (hb_font_get_variation_glyph (hb_font, c, 0xE0100 + (i - 16),
+                                      &variations[i]))
+       n++;
+      else
+       variations[i] = 0;
+    }
+
+  return n;
+}
+#endif /* HAVE_HARFBUZZ */
+
 #undef OTF_INT16_VAL
 #undef OTF_TAG_VAL
 #undef OTF_TAG
@@ -1191,17 +1473,32 @@ syms_of_w32uniscribe (void)
   pdumper_do_now_and_after_load (syms_of_w32uniscribe_for_pdumper);
 }
 
+#ifdef HAVE_HARFBUZZ
+static bool
+load_harfbuzz_funcs (HMODULE library)
+{
+  LOAD_DLL_FN (library, hb_blob_create);
+  LOAD_DLL_FN (library, hb_face_create_for_tables);
+  LOAD_DLL_FN (library, hb_face_get_glyph_count);
+  LOAD_DLL_FN (library, hb_font_create);
+  LOAD_DLL_FN (library, hb_font_destroy);
+  LOAD_DLL_FN (library, hb_face_get_upem);
+  LOAD_DLL_FN (library, hb_face_destroy);
+  LOAD_DLL_FN (library, hb_font_get_nominal_glyph);
+  LOAD_DLL_FN (library, hb_font_get_variation_glyph);
+  return hbfont_init_w32_funcs (library);
+}
+#endif /* HAVE_HARFBUZZ */
+
 static void
 syms_of_w32uniscribe_for_pdumper (void)
 {
-  HMODULE uniscribe;
-
-  /* Don't init uniscribe when dumping */
+  /* Don't init Uniscribe and HarfBuzz when dumping */
   if (!initialized)
     return;
 
-  /* Don't register if uniscribe is not available.  */
-  uniscribe = GetModuleHandle ("usp10");
+  /* Don't register if Uniscribe is not available.  */
+  HMODULE uniscribe = GetModuleHandle ("usp10");
   if (!uniscribe)
     return;
 
@@ -1221,4 +1518,31 @@ syms_of_w32uniscribe_for_pdumper (void)
     uniscribe_new_apis = true;
   else
     uniscribe_new_apis = false;
+
+#ifdef HAVE_HARFBUZZ
+  /* Currently, HarfBuzz DLLs are always named libharfbuzz-0.dll, as
+     the project keeps the ABI backward-compatible.  So we can
+     hard-code the name of the library here, for now.  If they ever
+     break ABI compatibility, we may need to load the DLL that
+     corresponds to the HarfBuzz version for which Emacs was built.  */
+  HMODULE harfbuzz = LoadLibrary ("libharfbuzz-0.dll");
+  /* Don't register if HarfBuzz is not available.  */
+  if (!harfbuzz)
+    return;
+
+  if (!load_harfbuzz_funcs (harfbuzz))
+    return;
+
+  harfbuzz_available = 1;
+  harfbuzz_font_driver = uniscribe_font_driver;
+  harfbuzz_font_driver.type = Qharfbuzz;
+  harfbuzz_font_driver.list = w32hb_list;
+  harfbuzz_font_driver.match = w32hb_match;
+  harfbuzz_font_driver.encode_char = w32hb_encode_char;
+  harfbuzz_font_driver.shape = hbfont_shape;
+  harfbuzz_font_driver.get_variation_glyphs = w32hb_get_variation_glyphs;
+  harfbuzz_font_driver.combining_capability = hbfont_combining_capability;
+  harfbuzz_font_driver.begin_hb_font = w32hb_begin_font;
+  register_font_driver (&harfbuzz_font_driver, NULL);
+#endif /* HAVE_HARFBUZZ */
 }
diff --git a/src/xdisp.c b/src/xdisp.c
index aa10abe..4031571 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -6955,6 +6955,7 @@ static next_element_function const 
get_next_element[NUM_IT_METHODS] =
    || ((IT)->cmp_it.stop_pos == (CHARPOS)                              \
        && composition_reseat_it (&(IT)->cmp_it, CHARPOS, BYTEPOS,      \
                                 END_CHARPOS, (IT)->w,                  \
+                                (IT)->bidi_it.resolved_level,          \
                                 FACE_FROM_ID_OR_NULL ((IT)->f,         \
                                                       (IT)->face_id),  \
                                 (IT)->string)))
diff --git a/src/xfns.c b/src/xfns.c
index 4195980..460dd13 100644
--- a/src/xfns.c
+++ b/src/xfns.c
@@ -3780,10 +3780,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 */
@@ -6205,15 +6211,21 @@ x_create_tip_frame (struct x_display_info *dpyinfo, 
Lisp_Object parms)
 
 #ifdef USE_CAIRO
   register_font_driver (&ftcrfont_driver, f);
+#ifdef HAVE_HARFBUZZ
+  register_font_driver (&ftcrhbfont_driver, f);
+#endif /* HAVE_HARFBUZZ */
 #else
-  register_font_driver (&xfont_driver, f);
 #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 */
 #endif /* HAVE_FREETYPE */
+  register_font_driver (&xfont_driver, f);
 #endif /* not USE_CAIRO */
 
   image_cache_refcount =
diff --git a/src/xftfont.c b/src/xftfont.c
index 4f0a0d8..04cda12 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];
@@ -174,10 +166,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;
 
@@ -273,6 +271,9 @@ xftfont_open (struct frame *f, Lisp_Object entity, int 
pixel_size)
   xftfont_info->maybe_otf = (ft_face->face_flags & FT_FACE_FLAG_SFNT) != 0;
   xftfont_info->otf = NULL;
 #endif /* HAVE_LIBOTF */
+#ifdef HAVE_HARFBUZZ
+  xftfont_info->hb_font = NULL;
+#endif /* HAVE_HARFBUZZ */
   xftfont_info->ft_size = ft_face->size;
 
   font->baseline_offset = 0;
@@ -312,6 +313,13 @@ xftfont_close (struct font *font)
       xftfont_info->otf = NULL;
     }
 #endif
+#ifdef HAVE_HARFBUZZ
+  if (xftfont_info->hb_font)
+    {
+      hb_font_destroy (xftfont_info->hb_font);
+      xftfont_info->hb_font = NULL;
+    }
+#endif
 
   /* See comment in xfont_close.  */
   if (xftfont_info->xftfont
@@ -503,13 +511,13 @@ xftfont_draw (struct glyph_string *s, int from, int to, 
int x, int y,
 
 #if defined HAVE_M17N_FLT && defined HAVE_LIBOTF
 static Lisp_Object
-xftfont_shape (Lisp_Object lgstring)
+xftfont_shape (Lisp_Object lgstring, Lisp_Object direction)
 {
   struct font *font = CHECK_FONT_GET_OBJECT (LGSTRING_FONT (lgstring));
   struct font_info *xftfont_info = (struct font_info *) font;
   FT_Face ft_face = XftLockFace (xftfont_info->xftfont);
   xftfont_info->ft_size = ft_face->size;
-  Lisp_Object val = ftfont_shape (lgstring);
+  Lisp_Object val = ftfont_shape (lgstring, direction);
   XftUnlockFace (xftfont_info->xftfont);
   return val;
 }
@@ -591,6 +599,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 =
@@ -618,7 +661,7 @@ struct font_driver const xftfont_driver =
 #if defined HAVE_M17N_FLT && defined HAVE_LIBOTF
   .shape = xftfont_shape,
 #endif
-#ifdef HAVE_OTF_GET_VARIATION_GLYPHS
+#if defined HAVE_OTF_GET_VARIATION_GLYPHS || defined 
HAVE_FT_FACE_GETCHARVARIANTINDEX
   .get_variation_glyphs = ftfont_variation_glyphs,
 #endif
   .filter_properties = ftfont_filter_properties,
@@ -626,11 +669,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 */
 
   DEFVAR_BOOL ("xft-font-ascent-descent-override",
               xft_font_ascent_descent_override,
@@ -645,4 +694,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 = hbfont_shape;
+  xfthbfont_driver.combining_capability = hbfont_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 */
 }



reply via email to

[Prev in Thread] Current Thread [Next in Thread]