emacs-diffs
[Top][All Lists]
Advanced

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

feature/android 6bd1cfa24fd 1/2: Update Android port


From: Po Lu
Subject: feature/android 6bd1cfa24fd 1/2: Update Android port
Date: Mon, 20 Mar 2023 02:51:05 -0400 (EDT)

branch: feature/android
commit 6bd1cfa24fd04de855e53e74b46cdf4047bced4c
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>

    Update Android port
    
    * configure.ac: Add support for HarfBuzz on Android.
    * java/INSTALL: Document where to get Emacs with HarfBuzz.
    * lisp/subr.el (overriding-text-conversion-style, y-or-n-p):
    Correctly set text conversion style if y-or-n-p is called inside
    the minibuffer.
    * src/sfnt.c (sfnt_read_cmap_format_8)
    (sfnt_read_cmap_format_12): Fix typos.
    (sfnt_read_24, sfnt_read_cmap_format_14): New function.
    (sfnt_read_cmap_table_1, sfnt_read_cmap_table): Handle format 14
    cmap tables.
    (sfnt_read_default_uvs_table, sfnt_read_nondefault_uvs_table)
    (sfnt_compare_table_offsets, sfnt_create_uvs_context)
    (sfnt_free_uvs_context, sfnt_compare_uvs_mapping)
    (sfnt_variation_glyph_for_char, sfnt_map_table, sfnt_unmap_table)
    (sfnt_read_table, sfnt_test_uvs): New functions.
    (main): Add UVS tests.
    * src/sfnt.h (struct sfnt_cmap_format_14)
    (struct sfnt_variation_selector_record)
    (struct sfnt_default_uvs_table, struct sfnt_unicode_value_range)
    (struct sfnt_nondefault_uvs_table, struct sfnt_uvs_mapping)
    (struct sfnt_mapped_variation_selector_record)
    (struct sfnt_table_offset_rec, struct sfnt_uvs_context)
    (struct sfnt_mapped_table): New structures.  Update prototypes.
    * src/sfntfont-android.c (android_sfntfont_driver): Register
    HarfBuzz callbacks where required.
    * src/sfntfont.c (sfntfont_select_cmap): Look for a format 14
    table.  Save it in new arg FORMAT14.
    (sfntfont_read_cmap): Adjust accordingly.
    (struct sfnt_font_info): New field `uvs'.  New fields `hb_font',
    `fd' and `directory'.
    (sfntfont_open): Open uvs context.  Under HarfBuzz, don't close
    the fd or subtable, but save them in the font info instead.
    (sfntfont_close): Free UVS context.  Close font fd and table
    directory and HarfBuzz font.
    (sfntfont_draw): Handle case where s->padding_p.
    (sfntfont_get_variation_glyphs): New function.
    (sfntfont_unmap_blob, sfntfont_get_font_table)
    (sfntfont_begin_hb_font): New functions.
    * src/sfntfont.h: Update prototypes.
    * src/textconv.c (Fset_text_conversion_style): Fix doc string.
---
 configure.ac           |   6 +-
 java/INSTALL           |  16 +-
 lisp/subr.el           |  16 +-
 src/sfnt.c             | 741 ++++++++++++++++++++++++++++++++++++++++++++++++-
 src/sfnt.h             | 165 ++++++++++-
 src/sfntfont-android.c |  12 +-
 src/sfntfont.c         | 384 ++++++++++++++++++++++++-
 src/sfntfont.h         |  11 +
 src/textconv.c         |   4 +-
 9 files changed, 1323 insertions(+), 32 deletions(-)

diff --git a/configure.ac b/configure.ac
index 44dbf60f938..47e5227a148 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1171,6 +1171,7 @@ package will likely install on older systems but crash on 
startup.])
   passthrough="$passthrough --with-lcms2=$with_lcms2"
   passthrough="$passthrough --with-mailutils=$with_mailutils"
   passthrough="$passthrough --with-pop=$with_pop"
+  passthrough="$passthrough --with-harfbuzz=$with_harfbuzz"
 
   AS_IF([test "x$with_mailutils" = "xyes"], [emacs_use_mailutils=yes])
   AC_SUBST([emacs_use_mailutils])
@@ -1255,13 +1256,13 @@ if test "$ANDROID" = "yes"; then
     with_lcms2=no
     with_mailutils=no
     with_pop=no
+    with_harfbuzz=no
   fi
 
   with_rsvg=no
   with_libsystemd=no
   with_cairo=no
   with_xft=no
-  with_harfbuzz=no
   with_libotf=no
   with_gpm=no
   with_dbus=no
@@ -4581,7 +4582,8 @@ else
 fi
 if test "${HAVE_X11}" = "yes" && test "${HAVE_FREETYPE}" = "yes" \
         || test "$window_system" = "pgtk" \
-        || test "${HAVE_W32}" = "yes"; then
+        || test "${HAVE_W32}" = "yes" \
+       || test "$REALLY_ANDROID" = "yes"; then
   if test "${with_harfbuzz}" != "no"; then
     EMACS_CHECK_MODULES([HARFBUZZ], [harfbuzz >= $harfbuzz_required_ver])
     if test "$HAVE_HARFBUZZ" = "yes"; then
diff --git a/java/INSTALL b/java/INSTALL
index 676c63a3cda..87d8979eb47 100644
--- a/java/INSTALL
+++ b/java/INSTALL
@@ -265,6 +265,8 @@ systems:
     (Extract and point ``--with-ndk-path'' to tiff-4.5.0-emacs.tar.gz.)
   tree-sitter  - https://sourceforge.net/projects/android-ports-for-gnu-emacs
     (Please see the section TREE-SITTER near the end of this file.)
+  harfbuzz     - https://sourceforge.net/projects/android-ports-for-gnu-emacs
+    (Please see the section HARFBUZZ near the end of this file.)
 
 And other developers have ported the following dependencies to Android
 systems:
@@ -305,15 +307,23 @@ should not try to build these packages separately using 
any
 TREE-SITTER
 
 A copy of tree-sitter modified to build with the ndk-build system can
-also find that URL.  To build Emacs with tree-sitter, you must unpack
-the following tar archive in that site:
+also be found that URL.  To build Emacs with tree-sitter, you must
+unpack the following tar archive in that site:
 
   tree-sitter-0.20.7-emacs.tar.gz
 
 and add the resulting folder to ``--with-ndk-build''.
 
 
-IMAGEMAGICK
+HARFBUZZ
+
+A copy of HarfBuzz modified to build with the ndk-build system can
+also be found at that URL.  To build Emacs with HarfBuzz, you must
+unpack the following tar archive in that site:
+
+  harfbuzz-7.1.0-emacs.tar.gz
+
+and add the resulting folder to ``--with-ndk-build''.
 
 There is a third party port of ImageMagick to Android.  Unfortunately,
 the port also uses its own patched versions of libpng, libjpeg,
diff --git a/lisp/subr.el b/lisp/subr.el
index e035ce51217..2f72945789b 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -3598,6 +3598,7 @@ character.  This is not possible when using `read-key', 
but using
 
 ;; Actually in textconv.c.
 (defvar overriding-text-conversion-style)
+(declare-function set-text-conversion-style "textconv.c")
 
 (defun y-or-n-p (prompt)
   "Ask user a \"y or n\" question.
@@ -3674,6 +3675,9 @@ like) while `y-or-n-p' is running)."
       (while
           (let* ((scroll-actions '(recenter scroll-up scroll-down
                                             scroll-other-window 
scroll-other-window-down))
+                 ;; Disable text conversion so that real key events
+                 ;; are sent.
+                 (overriding-text-conversion-style nil)
                  (key
                   (let ((cursor-in-echo-area t))
                     (when minibuffer-auto-raise
@@ -3721,9 +3725,15 @@ like) while `y-or-n-p' is running)."
                        map))
              ;; Protect this-command when called from pre-command-hook 
(bug#45029)
              (this-command this-command)
-             (str (read-from-minibuffer
-                   prompt nil keymap nil
-                   (or y-or-n-p-history-variable t))))
+             (str (progn
+                    (when (active-minibuffer-window)
+                      ;; If the minibuffer is already active, the
+                      ;; selected window might not change.  Disable
+                      ;; text conversion by hand.
+                      (set-text-conversion-style text-conversion-style))
+                    (read-from-minibuffer
+                     prompt nil keymap nil
+                     (or y-or-n-p-history-variable t)))))
         (setq answer (if (member str '("y" "Y")) 'act 'skip)))))
     (let ((ret (eq answer 'act)))
       (unless noninteractive
diff --git a/src/sfnt.c b/src/sfnt.c
index 5b219bf6369..bdd713aa016 100644
--- a/src/sfnt.c
+++ b/src/sfnt.c
@@ -601,7 +601,7 @@ sfnt_read_cmap_format_8 (int fd,
   ssize_t rc;
   uint32_t length, i;
 
-  /* Read the 32-bit lenth field.  */
+  /* Read the 32-bit length field.  */
   if (read (fd, &length, sizeof (length)) < sizeof (length))
     return (struct sfnt_cmap_format_8 *) -1;
 
@@ -693,7 +693,7 @@ sfnt_read_cmap_format_12 (int fd,
   ssize_t rc;
   uint32_t length, i;
 
-  /* Read the 32-bit lenth field.  */
+  /* Read the 32-bit length field.  */
   if (read (fd, &length, sizeof (length)) < sizeof (length))
     return (struct sfnt_cmap_format_12 *) -1;
 
@@ -773,6 +773,98 @@ sfnt_read_cmap_format_12 (int fd,
   return format12;
 }
 
+/* Read a 3-byte big endian number from BYTES.  */
+
+static unsigned int
+sfnt_read_24 (unsigned char *bytes)
+{
+  return (bytes[0] << 16u) | (bytes[1] << 8u) | bytes[2];
+}
+
+/* Read a format 14 cmap table from FD.  HEADER->format will be 14 and
+   HEADER->length will be 0; the 16-bit length field is not read.
+   OFFSET is the offset of the table's header in the font file.
+
+   Only variation selector records will be read.  UVS tables will
+   not.  */
+
+static struct sfnt_cmap_format_14 *
+sfnt_read_cmap_format_14 (int fd,
+                         struct sfnt_cmap_encoding_subtable_data *header,
+                         off_t offset)
+{
+  struct sfnt_cmap_format_14 *format14;
+  uint32_t length;
+  uint32_t num_records;
+  uint32_t buffer1[2];
+  size_t size, temp;
+  char buffer[3 + 4 + 4];
+  int i;
+
+  /* Read the length field and number of variation selector
+     records.  */
+
+  if (read (fd, buffer1, sizeof buffer1) < sizeof buffer1)
+    return NULL;
+
+  length = buffer1[0];
+  num_records = buffer1[1];
+
+  sfnt_swap32 (&length);
+  sfnt_swap32 (&num_records);
+
+  /* Now, the number of records present is known.  Allocate the format
+     14 cmap table.  */
+
+  size = sizeof *format14;
+  if (INT_MULTIPLY_WRAPV (num_records, sizeof *format14->records,
+                         &temp)
+      || INT_ADD_WRAPV (size, temp, &size))
+    return NULL;
+
+  format14 = xmalloc (size);
+
+  /* Fill in the data already read.  */
+  format14->format = header->format;
+  format14->length = length;
+  format14->num_var_selector_records = num_records;
+  format14->offset = offset;
+
+  /* Set the pointer to the remaining record data.  */
+  format14->records
+    = (struct sfnt_variation_selector_record *) (format14 + 1);
+
+  /* Read each variation selector record.  */
+
+  for (i = 0; i < num_records; ++i)
+    {
+      if (read (fd, buffer, sizeof buffer) < sizeof buffer)
+       {
+         xfree (format14);
+         return NULL;
+       }
+
+      /* First, read the 24 bit variation selector.  */
+      format14->records[i].var_selector
+       = sfnt_read_24 ((unsigned char *) buffer);
+
+      /* Next, read the two unaligned longs.  */
+      memcpy (&format14->records[i].default_uvs_offset,
+             buffer + 3,
+             sizeof format14->records[i].default_uvs_offset);
+      memcpy (&format14->records[i].nondefault_uvs_offset,
+             buffer + 7,
+             sizeof format14->records[i].nondefault_uvs_offset);
+
+      /* And swap them.  */
+      sfnt_swap32 (&format14->records[i].default_uvs_offset);
+      sfnt_swap32 (&format14->records[i].nondefault_uvs_offset);
+    }
+
+  /* Return the format 14 character mapping table.  */
+  return format14;
+}
+
 /* Read the CMAP subtable data from a given file FD at TABLE_OFFSET
    bytes from DIRECTORY_OFFSET.  Return the subtable data if it is
    supported.  Else, value is NULL if the format is unsupported, or -1
@@ -791,11 +883,26 @@ sfnt_read_cmap_table_1 (int fd, uint32_t directory_offset,
   if (lseek (fd, offset, SEEK_SET) == (off_t) -1)
     return (struct sfnt_cmap_encoding_subtable_data *) -1;
 
-  if (read (fd, &header, sizeof header) < sizeof header)
+  if (read (fd, &header.format, sizeof header.format)
+      < sizeof header.format)
     return (struct sfnt_cmap_encoding_subtable_data *) -1;
 
   sfnt_swap16 (&header.format);
-  sfnt_swap16 (&header.length);
+
+  /* Format 14 tables are rather special: they do not have a 16-bit
+     `length' field.  When these tables are encountered, leave reading
+     the rest of the header to `sfnt_read_cmap_table_14'.  */
+
+  if (header.format != 14)
+    {
+      if (read (fd, &header.length, sizeof header.length)
+         < sizeof header.length)
+       return (struct sfnt_cmap_encoding_subtable_data *) -1;
+
+      sfnt_swap16 (&header.length);
+    }
+  else
+    header.length = 0;
 
   switch (header.format)
     {
@@ -828,6 +935,10 @@ sfnt_read_cmap_table_1 (int fd, uint32_t directory_offset,
       return ((struct sfnt_cmap_encoding_subtable_data *)
              sfnt_read_cmap_format_12 (fd, &header));
 
+    case 14:
+      return ((struct sfnt_cmap_encoding_subtable_data *)
+             sfnt_read_cmap_format_14 (fd, &header, offset));
+
     default:
       return NULL;
     }
@@ -909,8 +1020,7 @@ sfnt_read_cmap_table (int fd, struct sfnt_offset_subtable 
*subtable,
     return cmap;
 
   /* Second, read each encoding subtable itself.  */
-  *data = xmalloc (cmap->num_subtables
-                  * sizeof *data);
+  *data = xmalloc (cmap->num_subtables * sizeof *data);
 
   for (i = 0; i < cmap->num_subtables; ++i)
     {
@@ -1199,7 +1309,10 @@ sfnt_lookup_glyph_12 (sfnt_char character,
 
 /* Look up the glyph index corresponding to the character CHARACTER,
    which must be in the correct encoding for the cmap table pointed to
-   by DATA.  */
+   by DATA.
+
+   DATA must be either a format 0, 2, 4, 6, 8 or 12 cmap table, else
+   behavior is undefined.  */
 
 TEST_STATIC sfnt_glyph
 sfnt_lookup_glyph (sfnt_char character,
@@ -11775,6 +11888,551 @@ sfnt_interpret_compound_glyph (struct sfnt_glyph 
*glyph,
 
 
 
+/* Unicode Variation Sequence (UVS) support.
+
+   Unicode defines a mechanism by which a two-codepoint sequence
+   consisting of a ``base character'' and ``variation selector'' is
+   able to produce a glyph that is a variant of the glyph that would
+   conventionally have been mapped to the ``base character''.
+
+   TrueType describes variation selector sequences through a type of
+   character mapping table that is given the format 14.  The character
+   mapping table consists of an array of variation selectors, each of
+   which have a corresponding ``default UVS table'', which describes
+   ranges of ``base characters'' having no special variant glyphs, and
+   a ``non-default UVS table'', which is a map of ``base characters''
+   to their corresponding variant glyphs.  */
+
+/* Read a default UVS table from the font file FD, at the specified
+   OFFSET.  Value is the default UVS table upon success, else
+   NULL.  */
+
+static struct sfnt_default_uvs_table *
+sfnt_read_default_uvs_table (int fd, off_t offset)
+{
+  struct sfnt_default_uvs_table *uvs;
+  uint32_t num_ranges, i, j;
+  size_t size, temp;
+  char data[512];
+
+  /* First, seek to the given offset.  */
+
+  if (lseek (fd, offset, SEEK_SET) != offset)
+    return NULL;
+
+  /* Next, read the number of ranges present.  */
+
+  if (read (fd, &num_ranges, sizeof num_ranges) != sizeof num_ranges)
+    return NULL;
+
+  /* Swap the number of ranges present.  */
+  sfnt_swap32 (&num_ranges);
+
+  /* Now, allocate enough to hold the UVS table.  */
+
+  size = sizeof *uvs;
+  if (INT_MULTIPLY_WRAPV (sizeof *uvs->ranges, num_ranges,
+                         &temp)
+      || INT_ADD_WRAPV (temp, size, &size))
+    return NULL;
+
+  uvs = xmalloc (size);
+
+  /* Fill in the data which was already read.  */
+  uvs->num_unicode_value_ranges = num_ranges;
+
+  /* Fill in the pointer to the ranges.  */
+  uvs->ranges = (struct sfnt_unicode_value_range *) (uvs + 1);
+  i = 0;
+
+  /* Read each default UVS range in multiples of 512 bytes.  Then,
+     fill in uvs->ranges.  */
+
+  while (num_ranges)
+    {
+      size = (num_ranges > 128 ? 512 : num_ranges * 4);
+
+      if (read (fd, data, size) != size)
+       {
+         xfree (uvs);
+         return NULL;
+       }
+
+      for (j = 0; j < size / 4; ++j)
+       {
+         uvs->ranges[i + j].start_unicode_value
+           = sfnt_read_24 ((unsigned char *) data + j * 4);
+         uvs->ranges[i + j].additional_count = data[j * 4 + 1];
+       }
+
+      i += j;
+      num_ranges -= size / 4;
+    }
+
+  /* Return the resulting default UVS table.  */
+  return uvs;
+}
+
+/* Read a non-default UVS table from the font file FD, at the
+   specified OFFSET.  Value is the non-default UVS table upon success,
+   else NULL.  */
+
+static struct sfnt_nondefault_uvs_table *
+sfnt_read_nondefault_uvs_table (int fd, off_t offset)
+{
+  struct sfnt_nondefault_uvs_table *uvs;
+  uint32_t num_mappings, i, j;
+  size_t size, temp;
+  char data[500];
+
+  /* First, seek to the given offset.  */
+
+  if (lseek (fd, offset, SEEK_SET) != offset)
+    return NULL;
+
+  /* Next, read the number of mappings present.  */
+
+  if (read (fd, &num_mappings, sizeof num_mappings)
+      != sizeof num_mappings)
+    return NULL;
+
+  /* Swap the number of mappings present.  */
+  sfnt_swap32 (&num_mappings);
+
+  /* Now, allocate enough to hold the UVS table.  */
+
+  size = sizeof *uvs;
+  if (INT_MULTIPLY_WRAPV (sizeof *uvs->mappings, num_mappings,
+                         &temp)
+      || INT_ADD_WRAPV (temp, size, &size))
+    return NULL;
+
+  uvs = xmalloc (size);
+
+  /* Fill in the data which was already read.  */
+  uvs->num_uvs_mappings = num_mappings;
+
+  /* Fill in the pointer to the mappings.  */
+  uvs->mappings = (struct sfnt_uvs_mapping *) (uvs + 1);
+
+  i = 0;
+
+  /* Read each nondefault UVS mapping in multiples of 500 bytes.
+     Then, fill in uvs->ranges.  */
+
+  while (num_mappings)
+    {
+      size = (num_mappings > 100 ? 500 : num_mappings * 5);
+
+      if (read (fd, data, size) != size)
+       {
+         xfree (uvs);
+         return NULL;
+       }
+
+      for (j = 0; j < size / 5; ++j)
+       {
+         uvs->mappings[i + j].unicode_value
+           = sfnt_read_24 ((unsigned char *) data + j * 5);
+         memcpy (&uvs->mappings[i + j].base_character_value,
+                 data + j * 5 + 3,
+                 sizeof uvs->mappings[i + j].base_character_value);
+         sfnt_swap16 (&uvs->mappings[i + j].base_character_value);
+       }
+
+      i += j;
+      num_mappings -= size / 5;
+    }
+
+  /* Return the nondefault UVS table.  */
+  return uvs;
+}
+
+/* Perform comparison of A and B, two table offsets.  */
+
+static int
+sfnt_compare_table_offsets (const void *a, const void *b)
+{
+  const struct sfnt_table_offset_rec *rec_a, *rec_b;
+
+  rec_a = a;
+  rec_b = b;
+
+  if (rec_a->offset < rec_b->offset)
+    return -1;
+  else if (rec_a->offset > rec_b->offset)
+    return 1;
+
+  return 0;
+}
+
+/* Create a variation selection context based on the format 14 cmap
+   subtable CMAP.
+
+   FD is the font file to which the table belongs.
+
+   Value is the variation selection context upon success, else NULL.
+   The context contains each variation selector record and their
+   associated default and nondefault UVS tables.  Free the context
+   with `sfnt_free_uvs_context'.  */
+
+TEST_STATIC struct sfnt_uvs_context *
+sfnt_create_uvs_context (struct sfnt_cmap_format_14 *cmap, int fd)
+{
+  struct sfnt_table_offset_rec *table_offsets, *rec, template;
+  size_t size, i, nmemb, j;
+  off_t offset;
+  struct sfnt_uvs_context *context;
+
+  if (INT_MULTIPLY_WRAPV (cmap->num_var_selector_records,
+                         sizeof *table_offsets, &size)
+      || INT_MULTIPLY_WRAPV (size, 2, &size))
+    return NULL;
+
+  context = NULL;
+
+  /* First, record and sort the UVS and nondefault UVS table offsets
+     in ascending order.  */
+
+  table_offsets = xmalloc (size);
+  memset (table_offsets, 0, size);
+  nmemb = cmap->num_var_selector_records * 2;
+  j = 0;
+
+  for (i = 0; i < cmap->num_var_selector_records; ++i)
+    {
+      /* Note that either offset may be 0, meaning there is no such
+        table.  */
+
+      if (cmap->records[i].default_uvs_offset)
+       {
+         if (INT_ADD_WRAPV (cmap->offset,
+                            cmap->records[i].default_uvs_offset,
+                            &table_offsets[j].offset))
+           goto bail;
+
+         table_offsets[j++].is_nondefault_table = false;
+       }
+
+      if (cmap->records[i].nondefault_uvs_offset)
+       {
+         if (INT_ADD_WRAPV (cmap->offset,
+                            cmap->records[i].nondefault_uvs_offset,
+                            &table_offsets[j].offset))
+           goto bail;
+
+         table_offsets[j++].is_nondefault_table = true;
+       }
+    }
+
+  /* Make nmemb the number of offsets actually looked up.  */
+  nmemb = j;
+
+  qsort (table_offsets, nmemb, sizeof *table_offsets,
+        sfnt_compare_table_offsets);
+
+  /* Now go through table_offsets, and read everything.  nmemb is the
+     number of elements in table_offsets[i]; it is kept up to date
+     when duplicate members are removed.  */
+  offset = -1;
+
+  for (i = 0; i < nmemb; ++i)
+    {
+      /* Skip past duplicate tables.  */
+
+      while (table_offsets[i].offset == offset && i < nmemb)
+       {
+         nmemb--;
+         table_offsets[i] = table_offsets[i + 1];
+       }
+
+      /* If the last element of the array is a duplicate, break out of
+        the loop.  */
+
+      if (i == nmemb)
+       break;
+
+      /* Read the correct type of table depending on
+        table_offsets[i].is_nondefault_table.  Then skip past
+        duplicate tables.  Don't handle the case where two different
+        kind of tables share the same offset, because that is not
+        possible in a valid variation selector record.  */
+
+      offset = table_offsets[i].offset;
+
+      if (table_offsets[i].is_nondefault_table)
+       table_offsets[i].table
+         = sfnt_read_nondefault_uvs_table (fd, offset);
+      else
+       table_offsets[i].table
+         = sfnt_read_default_uvs_table (fd, offset);
+    }
+
+  /* Now make the context.  */
+  context = xmalloc (sizeof *context);
+  context->num_records = cmap->num_var_selector_records;
+  context->nmemb = nmemb;
+  context->records = xmalloc (sizeof *context->records
+                             * cmap->num_var_selector_records);
+
+  for (i = 0; i < cmap->num_var_selector_records; ++i)
+    {
+      context->records[i].selector = cmap->records[i].var_selector;
+
+      /* Either offset may be 0, meaning no such table exists.  Also,
+         the code below will lose if more than one kind of table
+         shares the same offset, because that is impossible.  */
+
+      if (cmap->records[i].default_uvs_offset)
+       {
+         /* Resolve the default table.  */
+         template.offset = (cmap->records[i].default_uvs_offset
+                            + cmap->offset);
+         rec = bsearch (&template, table_offsets,
+                        nmemb, sizeof *table_offsets,
+                        sfnt_compare_table_offsets);
+
+         /* Make sure this record is the right type.  */
+         if (!rec || rec->is_nondefault_table || !rec->table)
+           goto bail;
+
+         context->records[i].default_uvs = rec->table;
+       }
+      else
+       context->records[i].default_uvs = NULL;
+
+      if (cmap->records[i].nondefault_uvs_offset)
+       {
+         /* Resolve the nondefault table.  */
+         template.offset = (cmap->records[i].nondefault_uvs_offset
+                            + cmap->offset);
+         rec = bsearch (&template, table_offsets,
+                        nmemb, sizeof *table_offsets,
+                        sfnt_compare_table_offsets);
+
+         if (!rec)
+           goto bail;
+
+         /* Make sure this record is the right type.  */
+         if (!rec || !rec->is_nondefault_table || !rec->table)
+           goto bail;
+
+         context->records[i].nondefault_uvs = rec->table;
+       }
+      else
+       context->records[i].nondefault_uvs = NULL;
+    }
+
+  context->tables = table_offsets;
+  return context;
+
+ bail:
+
+  if (context)
+    {
+      xfree (context->records);
+      xfree (context);
+    }
+
+  /* Loop through and free any tables that might have been read
+     already.  */
+
+  for (i = 0; i < nmemb; ++i)
+    xfree (table_offsets[i].table);
+
+  xfree (table_offsets);
+  return NULL;
+}
+
+/* Free the specified variation selection context C.  */
+
+TEST_STATIC void
+sfnt_free_uvs_context (struct sfnt_uvs_context *c)
+{
+  size_t i;
+
+  xfree (c->records);
+
+  for (i = 0; i < c->nmemb; ++i)
+    xfree (c->tables[i].table);
+
+  xfree (c->tables);
+  xfree (c);
+}
+
+/* Compare *(sfnt_char *) K to ((struct sfnt_uvs_mapping *)
+   V)->unicode_value appropriately for bsearch.  */
+
+static int
+sfnt_compare_uvs_mapping (const void *k, const void *v)
+{
+  const sfnt_char *key;
+  const struct sfnt_uvs_mapping *value;
+
+  key = k;
+  value = v;
+
+  if (*key < value->unicode_value)
+    return -1;
+  else if (*key == value->unicode_value)
+    return 0;
+
+  return 1;
+}
+
+/* Return the ID of a variation glyph for the character C in the
+   nondefault UVS mapping table UVS.
+
+   Value is the glyph ID upon success, or 0 if there is no variation
+   glyph for the base character C.  */
+
+TEST_STATIC sfnt_glyph
+sfnt_variation_glyph_for_char (struct sfnt_nondefault_uvs_table *uvs,
+                              sfnt_char c)
+{
+  struct sfnt_uvs_mapping *mapping;
+
+  mapping = bsearch (&c, uvs->mappings, uvs->num_uvs_mappings,
+                    sizeof *uvs->mappings,
+                    sfnt_compare_uvs_mapping);
+
+  return mapping ? mapping->base_character_value : 0;
+}
+
+
+
+#if defined HAVE_MMAP && !defined TEST
+
+/* Memory mapping support.
+   It useful to map OpenType layout tables prior to using them in
+   an external shaping engine such as HarfBuzz.  */
+
+/* Map a table identified by TAG into the structure *TABLE.
+   TAG is swapped into host byte order.
+
+   Use the table directory SUBTABLE, which corresponds to the font
+   file FD.
+
+   Return 0 upon success, and set TABLE->data to the table data,
+   TABLE->mapping to the start of the mapped area, TABLE->length to
+   the length of the table contents, and TABLE->size to the size of
+   the mapping.
+
+   Return 1 upon failure.  */
+
+int
+sfnt_map_table (int fd, struct sfnt_offset_subtable *subtable,
+               uint32_t tag, struct sfnt_mapped_table *table)
+{
+  struct sfnt_table_directory *directory;
+  size_t offset, page, map_offset;
+  void *data;
+  int i;
+
+  /* Find the table in the directory.  */
+
+  for (i = 0; i < subtable->num_tables; ++i)
+    {
+      if (subtable->subtables[i].tag == tag)
+       {
+         directory = &subtable->subtables[i];
+         break;
+       }
+    }
+
+  if (i == subtable->num_tables)
+    return 1;
+
+  /* Now try to map the glyph data.  Make sure offset is a multiple of
+     the page size.  */
+
+  page = getpagesize ();
+  offset = directory->offset & ~(page - 1);
+
+  /* Figure out how much larger the mapping should be.  */
+  map_offset = directory->offset - offset;
+
+  /* Do the mmap.  */
+  data = mmap (NULL, directory->length + map_offset,
+              PROT_READ, MAP_PRIVATE, fd, offset);
+
+  if (data == MAP_FAILED)
+    return 1;
+
+  /* Fill in *TABLE.  */
+  table->data = (unsigned char *) data + map_offset;
+  table->mapping = data;
+  table->length = directory->length;
+  table->size = directory->length + map_offset;
+  return 0;
+}
+
+/* Unmap the table inside *TABLE.
+   Value is 0 upon success, 1 otherwise.  */
+
+int
+sfnt_unmap_table (struct sfnt_mapped_table *table)
+{
+  return munmap (table->mapping, table->size) != 0;
+}
+
+#endif /* HAVE_MMAP && !TEST */
+
+
+
+#ifndef TEST
+
+/* Reading table contents.  */
+
+/* Read the table with the specified TAG from the font file FD.
+   Return its length in *LENGTH, and its data upon success, else
+   NULL.  */
+
+void *
+sfnt_read_table (int fd, struct sfnt_offset_subtable *subtable,
+                uint32_t tag, size_t *length)
+{
+  struct sfnt_table_directory *directory;
+  void *data;
+  int i;
+
+  /* Find the table in the directory.  */
+
+  for (i = 0; i < subtable->num_tables; ++i)
+    {
+      if (subtable->subtables[i].tag == tag)
+       {
+         directory = &subtable->subtables[i];
+         break;
+       }
+    }
+
+  if (i == subtable->num_tables)
+    return NULL;
+
+  /* Seek to the table.  */
+
+  if (lseek (fd, directory->offset, SEEK_SET) != directory->offset)
+    return NULL;
+
+  /* Now allocate enough to hold the data and read into it.  */
+
+  data = xmalloc (directory->length);
+  if (read (fd, data, directory->length) != directory->length)
+    {
+      xfree (data);
+      return NULL;
+    }
+
+  /* Return the length and table data.  */
+  *length = directory->length;
+  return data;
+}
+
+#endif /* !TEST */
+
+
+
 #ifdef TEST
 
 struct sfnt_test_dcontext
@@ -15494,6 +16152,52 @@ sfnt_pop_hook (struct sfnt_interpreter *interpreter,
 
 
 
+static void
+sfnt_test_uvs (int fd, struct sfnt_cmap_format_14 *format14)
+{
+  struct sfnt_uvs_context *context;
+  size_t i, j;
+  sfnt_glyph glyph;
+  sfnt_char c;
+  struct sfnt_nondefault_uvs_table *uvs;
+
+  context = sfnt_create_uvs_context (format14, fd);
+
+  /* Print each variation selector and its associated ranges.  */
+
+  if (!context)
+    fprintf (stderr, "failed to read uvs data\n");
+  else
+    {
+      fprintf (stderr, "UVS context with %zu records and %zu tables\n",
+              context->num_records, context->nmemb);
+
+      for (i = 0; i < context->num_records; ++i)
+       {
+         if (!context->records[i].nondefault_uvs)
+           continue;
+
+         uvs = context->records[i].nondefault_uvs;
+
+         for (j = 0; j < uvs->num_uvs_mappings; ++j)
+           {
+             c = uvs->mappings[j].unicode_value;
+             glyph = sfnt_variation_glyph_for_char (uvs, c);
+
+             if (glyph != uvs->mappings[j].base_character_value)
+               abort ();
+
+             fprintf (stderr, "   UVS: %"PRIx32" (%"PRIx32") -> %"PRIu32"\n",
+                      c, context->records[i].selector, glyph);
+           }
+       }
+
+      sfnt_free_uvs_context (context);
+    }
+}
+
+
+
 /* Main entry point.  */
 
 /* Simple tests that were used while developing this file.  By the
@@ -15564,7 +16268,7 @@ main (int argc, char **argv)
   struct sfnt_raster **rasters;
   size_t length;
 
-  if (argc != 2)
+  if (argc < 2)
     return 1;
 
   if (!strcmp (argv[1], "--check-interpreter"))
@@ -15654,8 +16358,25 @@ main (int argc, char **argv)
                 data[i]->format);
     }
 
-#define FANCY_PPEM 12
-#define EASY_PPEM  12
+  if (argc >= 3 && !strcmp (argv[2], "--check-variation-selectors"))
+    {
+      /* Look for a format 14 cmap table.  */
+
+      for (i = 0; i < table->num_subtables; ++i)
+       {
+         if (data[i]->format == 14)
+           {
+             fprintf (stderr, "format 14 subtable found\n");
+             sfnt_test_uvs (fd, (struct sfnt_cmap_format_14 *) data[i]);
+             return 0;
+           }
+       }
+
+      return 1;
+    }
+
+#define FANCY_PPEM 25
+#define EASY_PPEM  25
 
   interpreter = NULL;
   head = sfnt_read_head_table (fd, font);
diff --git a/src/sfnt.h b/src/sfnt.h
index 4bf46b62397..83d34bfb757 100644
--- a/src/sfnt.h
+++ b/src/sfnt.h
@@ -25,6 +25,8 @@ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
USA.  */
 #include <stddef.h>
 #include <setjmp.h>
 
+#include <sys/types.h>
+
 #if defined emacs || defined TEST
 #define SFNT_ENABLE_HINTING
 #endif
@@ -422,7 +424,7 @@ struct sfnt_cmap_format_8
   struct sfnt_cmap_format_8_or_12_group *groups;
 };
 
-/* cmap formats 10, 13 and 14 unsupported.  */
+/* cmap formats 10, 13 unsupported.  */
 
 struct sfnt_cmap_format_12
 {
@@ -445,6 +447,36 @@ struct sfnt_cmap_format_12
   struct sfnt_cmap_format_8_or_12_group *groups;
 };
 
+struct sfnt_cmap_format_14
+{
+  /* Format, set to 14.  */
+  uint16_t format;
+
+  /* The length of the table in bytes.  */
+  uint32_t length;
+
+  /* Number of variation selector records.  */
+  uint16_t num_var_selector_records;
+
+  /* The offset of this table in the font file.  */
+  off_t offset;
+
+  /* Variable length data.  */
+  struct sfnt_variation_selector_record *records;
+};
+
+struct sfnt_variation_selector_record
+{
+  /* 24-bit unsigned variation selector.  */
+  unsigned int var_selector;
+
+  /* Offset to default UVS table.  */
+  uint32_t default_uvs_offset;
+
+  /* Offset to non-default UVS table.  */
+  uint32_t nondefault_uvs_offset;
+};
+
 struct sfnt_maxp_table
 {
   /* Table version.  */
@@ -1437,6 +1469,106 @@ struct sfnt_instructed_outline
 
 
 
+/* Unicode Variation Sequence (UVS) support.  */
+
+struct sfnt_default_uvs_table
+{
+  /* Number of ranges that follow.  */
+  uint32_t num_unicode_value_ranges;
+
+  /* Variable length data.  */
+  struct sfnt_unicode_value_range *ranges;
+};
+
+struct sfnt_unicode_value_range
+{
+  /* First value in this range.  */
+  unsigned int start_unicode_value;
+
+  /* Number of additional values in this range.  */
+  unsigned char additional_count;
+};
+
+struct sfnt_nondefault_uvs_table
+{
+  /* Number of UVS mappings which follow.  */
+  uint32_t num_uvs_mappings;
+
+  /* Variable length data.  */
+  struct sfnt_uvs_mapping *mappings;
+};
+
+struct sfnt_uvs_mapping
+{
+  /* Base character value.  */
+  unsigned int unicode_value;
+
+  /* Glyph ID of the base character value.  */
+  uint16_t base_character_value;
+};
+
+struct sfnt_mapped_variation_selector_record
+{
+  /* The variation selector.  */
+  unsigned int selector;
+
+  /* Its default UVS table.  */
+  struct sfnt_default_uvs_table *default_uvs;
+
+  /* Its nondefault UVS table.  */
+  struct sfnt_nondefault_uvs_table *nondefault_uvs;
+};
+
+/* Structure describing a single offset to load into a variation
+   selection context.  */
+
+struct sfnt_table_offset_rec
+{
+  /* The offset from the start of the font file.  */
+  off_t offset;
+
+  /* Whether or not the offset points to a non-default UVS table.  */
+  bool is_nondefault_table;
+
+  /* Pointer to the UVS table.  */
+  void *table;
+};
+
+struct sfnt_uvs_context
+{
+  /* Number of records and tables.  */
+  size_t num_records, nmemb;
+
+  /* Array of UVS tables.  */
+  struct sfnt_table_offset_rec *tables;
+
+  /* Array of variation selector records mapped to
+     their corresponding tables.  */
+  struct sfnt_mapped_variation_selector_record *records;
+};
+
+
+
+#if defined HAVE_MMAP && !defined TEST
+
+/* Memory mapping support.  */
+
+struct sfnt_mapped_table
+{
+  /* Pointer to table data.  */
+  void *data;
+
+  /* Pointer to table mapping.  */
+  void *mapping;
+
+  /* Size of mapped data and size of mapping.  */
+  size_t length, size;
+};
+
+#endif /* HAVE_MMAP && !TEST */
+
+
+
 /* Functions used to read tables used by the TrueType interpreter.  */
 
 #ifndef TEST
@@ -1509,6 +1641,37 @@ extern const char *sfnt_interpret_compound_glyph 
(PROTOTYPE);
 
 #undef PROTOTYPE
 
+
+
+#define PROTOTYPE struct sfnt_cmap_format_14 *, int
+
+extern struct sfnt_uvs_context *sfnt_create_uvs_context (PROTOTYPE);
+
+#undef PROTOTYPE
+
+extern void sfnt_free_uvs_context (struct sfnt_uvs_context *);
+
+#define PROTOTYPE struct sfnt_nondefault_uvs_table *, sfnt_char
+
+extern sfnt_glyph sfnt_variation_glyph_for_char (PROTOTYPE);
+
+#undef PROTOTYPE
+
+
+
+#ifdef HAVE_MMAP
+
+extern int sfnt_map_table (int, struct sfnt_offset_subtable *,
+                          uint32_t, struct sfnt_mapped_table *);
+extern int sfnt_unmap_table (struct sfnt_mapped_table *);
+
+#endif /* HAVE_MMAP */
+
+
+
+extern void *sfnt_read_table (int, struct sfnt_offset_subtable *,
+                             uint32_t, size_t *);
+
 #endif /* TEST */
 
 
diff --git a/src/sfntfont-android.c b/src/sfntfont-android.c
index 8324185cc6f..37f43465097 100644
--- a/src/sfntfont-android.c
+++ b/src/sfntfont-android.c
@@ -657,8 +657,16 @@ const struct font_driver android_sfntfont_driver =
     .encode_char = sfntfont_encode_char,
     .text_extents = sfntfont_text_extents,
     .list_family = sfntfont_list_family,
-
-    /* TODO: list_family, shaping.  */
+    .get_variation_glyphs = sfntfont_get_variation_glyphs,
+
+#ifdef HAVE_HARFBUZZ
+    /* HarfBuzz support is enabled transparently on Android without
+       using a separate font driver.  */
+    .begin_hb_font = sfntfont_begin_hb_font,
+    .combining_capability = hbfont_combining_capability,
+    .shape = hbfont_shape,
+    .otf_capability = hbfont_otf_capability,
+#endif /* HAVE_HARFBUZZ */
   };
 
 
diff --git a/src/sfntfont.c b/src/sfntfont.c
index b8ffce27062..500256d6fb4 100644
--- a/src/sfntfont.c
+++ b/src/sfntfont.c
@@ -34,6 +34,11 @@ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
USA.  */
 #include "sfnt.h"
 #include "sfntfont.h"
 
+#ifdef HAVE_HARFBUZZ
+#include <hb.h>
+#include <hb-ot.h>
+#endif /* HAVE_HARFBUZZ */
+
 /* For FRAME_FONT.  */
 #include TERM_HEADER
 
@@ -1038,15 +1043,20 @@ sfntfont_charset_for_cmap (struct 
sfnt_cmap_encoding_subtable subtable)
 
 /* Pick the best character map in the cmap table CMAP.  Use the
    subtables in SUBTABLES and DATA.  Return the subtable data and the
-   subtable in *SUBTABLE upon success, NULL otherwise.  */
+   subtable in *SUBTABLE upon success, NULL otherwise.
+
+   If FORMAT14 is non-NULL, return any associated format 14 variation
+   selection context in *FORMAT14 should the selected charcter map be
+   a Unicode character map.  */
 
 static struct sfnt_cmap_encoding_subtable_data *
 sfntfont_select_cmap (struct sfnt_cmap_table *cmap,
                      struct sfnt_cmap_encoding_subtable *subtables,
                      struct sfnt_cmap_encoding_subtable_data **data,
-                     struct sfnt_cmap_encoding_subtable *subtable)
+                     struct sfnt_cmap_encoding_subtable *subtable,
+                     struct sfnt_cmap_format_14 **format14)
 {
-  int i;
+  int i, j;
 
   /* First look for a non-BMP Unicode cmap.  */
 
@@ -1055,6 +1065,24 @@ sfntfont_select_cmap (struct sfnt_cmap_table *cmap,
       if (data[i] && sfntfont_identify_cmap (subtables[i]) == 2)
        {
          *subtable = subtables[i];
+
+         if (!format14)
+           return data[i];
+
+         /* Search for a correspoinding format 14 character map.
+            This is used in conjunction with the selected character
+            map to map variation sequences.  */
+
+         for (j = 0; j < cmap->num_subtables; ++j)
+           {
+             if (data[j]
+                 && subtables[j].platform_id == SFNT_PLATFORM_UNICODE
+                 && (subtables[j].platform_specific_id
+                     == SFNT_UNICODE_VARIATION_SEQUENCES)
+                 && data[j]->format == 14)
+               *format14 = (struct sfnt_cmap_format_14 *) data[j];
+           }
+
          return data[i];
        }
     }
@@ -1066,6 +1094,24 @@ sfntfont_select_cmap (struct sfnt_cmap_table *cmap,
       if (data[i] && sfntfont_identify_cmap (subtables[i]) == 1)
        {
          *subtable = subtables[i];
+
+         if (!format14)
+           return data[i];
+
+         /* Search for a correspoinding format 14 character map.
+            This is used in conjunction with the selected character
+            map to map variation sequences.  */
+
+         for (j = 0; j < cmap->num_subtables; ++j)
+           {
+             if (data[j]
+                 && subtables[j].platform_id == SFNT_PLATFORM_UNICODE
+                 && (subtables[j].platform_specific_id
+                     == SFNT_UNICODE_VARIATION_SEQUENCES)
+                 && data[j]->format == 14)
+               *format14 = (struct sfnt_cmap_format_14 *) data[j];
+           }
+
          return data[i];
        }
     }
@@ -1128,7 +1174,7 @@ sfntfont_read_cmap (struct sfnt_font_desc *desc,
   /* Now pick the best character map.  */
 
   *cmap = sfntfont_select_cmap (table, subtables, data,
-                               subtable);
+                               subtable, NULL);
 
   /* Free the cmap data.  */
 
@@ -1960,6 +2006,9 @@ struct sfnt_font_info
   /* Data identifying that character map.  */
   struct sfnt_cmap_encoding_subtable cmap_subtable;
 
+  /* The UVS context.  */
+  struct sfnt_uvs_context *uvs;
+
   /* Outline cache.  */
   struct sfnt_outline_cache outline_cache;
 
@@ -1983,6 +2032,17 @@ struct sfnt_font_info
   /* Whether or not the glyph table has been mmapped.  */
   bool glyf_table_mapped;
 #endif /* HAVE_MMAP */
+
+#ifdef HAVE_HARFBUZZ
+  /* HarfBuzz font object.  */
+  hb_font_t *hb_font;
+
+  /* File descriptor associated with this font.  */
+  int fd;
+
+  /* The table directory of the font file.  */
+  struct sfnt_offset_subtable *directory;
+#endif /* HAVE_HARFBUZZ */
 };
 
 #ifdef HAVE_MMAP
@@ -2198,6 +2258,7 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity,
   struct charset *charset;
   int point_size;
   Display_Info *dpyinfo;
+  struct sfnt_cmap_format_14 *format14;
 
   if (XFIXNUM (AREF (font_entity, FONT_SIZE_INDEX)) != 0)
     pixel_size = XFIXNUM (AREF (font_entity, FONT_SIZE_INDEX));
@@ -2240,6 +2301,7 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity,
   font_info->prep = NULL;
   font_info->fpgm = NULL;
   font_info->cvt = NULL;
+  font_info->uvs = NULL;
 
   font_info->outline_cache.next = &font_info->outline_cache;
   font_info->outline_cache.last = &font_info->outline_cache;
@@ -2251,6 +2313,11 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity,
 #ifdef HAVE_MMAP
   font_info->glyf_table_mapped = false;
 #endif /* HAVE_MMAP */
+#ifdef HAVE_HARFBUZZ
+  font_info->hb_font = NULL;
+  font_info->fd = -1;
+  font_info->directory = NULL;
+#endif /* HAVE_HARFBUZZ */
 
   /* Open the font.  */
   fd = emacs_open (desc->path, O_RDONLY, 0);
@@ -2280,14 +2347,29 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity,
   if (!font_info->cmap)
     goto bail2;
 
+  format14 = NULL;
   font_info->cmap_data
     = sfntfont_select_cmap (font_info->cmap,
                            subtables, data,
-                           &font_info->cmap_subtable);
+                           &font_info->cmap_subtable,
+                           &format14);
+
+  if (format14)
+    {
+      /* Build a UVS context from this format 14 mapping table.  A UVS
+         context contains each variation selector supported by the
+         font, and a list of ``non-default'' mappings between base
+         characters and variation glyph IDs.  */
+
+      font_info->uvs = sfnt_create_uvs_context (format14, fd);
+      xfree (format14);
+    }
 
   for (i = 0; i < font_info->cmap->num_subtables; ++i)
     {
-      if (data[i] != font_info->cmap_data)
+      if (data[i] != font_info->cmap_data
+         /* format14 has already been freed.  */
+         && data[i] != (struct sfnt_cmap_encoding_subtable_data *) format14)
        xfree (data[i]);
     }
 
@@ -2432,11 +2514,19 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity,
   sfntfont_setup_interpreter (fd, font_info, subtable,
                              point_size);
 
+#ifndef HAVE_HARFBUZZ
   /* Close the font file descriptor.  */
   emacs_close (fd);
 
   /* Free the offset subtable.  */
   xfree (subtable);
+#else /* HAVE_HARFBUZZ */
+  /* HarfBuzz will potentially read font tables after the font has
+     been opened by Emacs.  Keep the font open, and record its offset
+     subtable.  */
+  font_info->fd = fd;
+  font_info->directory = subtable;
+#endif /* !HAVE_HARFBUZZ */
 
 #ifdef HAVE_MMAP
   /* Link the font onto the font table.  */
@@ -2483,6 +2573,10 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity,
   xfree (font_info->cmap_data);
   font_info->cmap_data = NULL;
  bail3:
+
+  if (font_info->uvs)
+    sfnt_free_uvs_context (font_info->uvs);
+
   xfree (font_info->cmap);
   font_info->cmap = NULL;
  bail2:
@@ -2677,8 +2771,7 @@ sfntfont_close (struct font *font)
   xfree (info->hmtx);
 
 #ifdef HAVE_MMAP
-  if (info->glyf_table_mapped
-      && info->glyf)
+  if (info->glyf_table_mapped && info->glyf)
     {
       rc = sfnt_unmap_glyf_table (info->glyf);
 
@@ -2697,6 +2790,12 @@ sfntfont_close (struct font *font)
   xfree (info->cvt);
   xfree (info->interpreter);
 
+  /* Deallocate any UVS context allocated to look up font variation
+     sequences.  */
+
+  if (info->uvs)
+    sfnt_free_uvs_context (info->uvs);
+
   /* Clear these fields.  It seems that close can be called twice,
      once during font driver destruction, and once during GC.  */
 
@@ -2713,6 +2812,7 @@ sfntfont_close (struct font *font)
   info->fpgm = NULL;
   info->cvt = NULL;
   info->interpreter = NULL;
+  info->uvs = NULL;
 
 #ifdef HAVE_MMAP
 
@@ -2728,6 +2828,28 @@ sfntfont_close (struct font *font)
 
 #endif /* HAVE_MMAP */
 
+#ifdef HAVE_HARFBUZZ
+  /* Close the font file.  */
+
+  if (info->fd != -1)
+    {
+      emacs_close (info->fd);
+      info->fd = -1;
+    }
+
+  /* Free its table directory.  */
+  xfree (info->directory);
+  info->directory = NULL;
+
+  /* Free any hb_font created.  */
+
+  if (info->hb_font)
+    {
+      hb_font_destroy (info->hb_font);
+      info->hb_font = NULL;
+    }
+#endif
+
   sfntfont_free_outline_cache (&info->outline_cache);
   sfntfont_free_raster_cache (&info->raster_cache);
 }
@@ -2821,7 +2943,11 @@ sfntfont_draw (struct glyph_string *s, int from, int to,
 
       /* Now work out where to put the outline.  */
       x_coords[i - from] = current_x;
-      current_x += SFNT_CEIL_FIXED (metrics.advance) >> 16;
+
+      if (s->padding_p)
+       current_x += 1;
+      else
+       current_x += SFNT_CEIL_FIXED (metrics.advance) >> 16;
     }
 
   /* Call the window system function to put the glyphs to the
@@ -2865,6 +2991,126 @@ sfntfont_list_family (struct frame *f)
 
 
 
+/* Unicode Variation Selector (UVS) support.  This is typically
+   required for Harfbuzz.  */
+
+/* Given a FONT object, a character C, and VARIATIONS, return the
+   number of non-default variation glyphs, and their glyph ids in
+   VARIATIONS.
+
+   For each variation selector character K with a non-default glyph in
+   the variation selector range 0xFE00 to 0xFE0F, set variations[K -
+   0xFE0] to its ID.
+
+   For each variation selector character K with a non-default glyph in
+   the variation selector range 0xE0100 to 0xE01EF, set variations[K -
+   0xE0100 + 16] to its ID.
+
+   If value is more than 0, set all other members of VARIATIONS to 0.
+   Else, the contents of VARIATIONS are undefined.  */
+
+int
+sfntfont_get_variation_glyphs (struct font *font, int c,
+                              unsigned variations[256])
+{
+  struct sfnt_font_info *info;
+  size_t i;
+  int n;
+  struct sfnt_mapped_variation_selector_record *record;
+
+  info = (struct sfnt_font_info *) font;
+  n = 0;
+
+  /* Return 0 if there is no UVS mapping table.  */
+
+  if (!info->uvs)
+    return 0;
+
+  /* Clear the variations array.  */
+
+  memset (variations, 0, sizeof *variations * 256);
+
+  /* Find the first 0xFExx selector.  */
+
+  i = 0;
+  while (i < info->uvs->num_records
+        && info->uvs->records[i].selector < 0xfe00)
+    ++i;
+
+  /* Fill in selectors 0 to 15.  */
+
+  while (i < info->uvs->num_records
+        && info->uvs->records[i].selector <= 0xfe0f)
+    {
+      record = &info->uvs->records[i];
+
+      /* If record has no non-default mappings, continue on to the
+        next selector.  */
+
+      if (!record->nondefault_uvs)
+       goto next_selector;
+
+      /* Handle invalid unsorted tables.  */
+
+      if (record->selector < 0xfe00)
+       return 0;
+
+      /* Find the glyph ID associated with C and put it in
+        VARIATIONS.  */
+
+      variations[info->uvs->records[i].selector - 0xfe00]
+       = sfnt_variation_glyph_for_char (record->nondefault_uvs, c);
+
+      if (variations[info->uvs->records[i].selector - 0xfe00])
+       ++n;
+
+    next_selector:
+      ++i;
+    }
+
+  /* Find the first 0xE0100 selector.  */
+
+  i = 0;
+  while (i < info->uvs->num_records
+        && info->uvs->records[i].selector < 0xe0100)
+    ++i;
+
+  /* Fill in selectors 16 to 255.  */
+
+  while (i < info->uvs->num_records
+        && info->uvs->records[i].selector <= 0xe01ef)
+    {
+      record = &info->uvs->records[i];
+
+      /* If record has no non-default mappings, continue on to the
+        next selector.  */
+
+      if (!record->nondefault_uvs)
+       goto next_selector_1;
+
+      /* Handle invalid unsorted tables.  */
+
+      if (record->selector < 0xe0100)
+       return 0;
+
+      /* Find the glyph ID associated with C and put it in
+        VARIATIONS.  */
+
+      variations[info->uvs->records[i].selector - 0xe0100 + 16]
+       = sfnt_variation_glyph_for_char (record->nondefault_uvs, c);
+
+      if (variations[info->uvs->records[i].selector - 0xe0100 + 16])
+       ++n;
+
+    next_selector_1:
+      ++i;
+    }
+
+  return n;
+}
+
+
+
 /* mmap specific stuff.  */
 
 #ifdef HAVE_MMAP
@@ -2893,6 +3139,126 @@ sfntfont_detect_sigbus (void *addr)
 
 
 
+/* Harfbuzz font support.  */
+
+#ifdef HAVE_HARFBUZZ
+
+#ifdef HAVE_MMAP
+
+/* Unmap the specified table.  */
+
+static void
+sfntfont_unmap_blob (void *ptr)
+{
+  if (sfnt_unmap_table (ptr))
+    emacs_abort ();
+
+  xfree (ptr);
+}
+
+#endif /* HAVE_MMAP */
+
+/* Given a font DATA and a tag TAG, return the data of the
+   corresponding font table as a HarfBuzz blob.  */
+
+static hb_blob_t *
+sfntfont_get_font_table (hb_face_t *face, hb_tag_t tag, void *data)
+{
+  size_t size;
+  struct sfnt_font_info *info;
+#ifdef HAVE_MMAP
+  struct sfnt_mapped_table *table;
+  hb_blob_t *blob;
+
+  info = data;
+  table = xmalloc (sizeof *table);
+
+  if (!sfnt_map_table (info->fd, info->directory, tag,
+                      table))
+    {
+      /* Create an hb_blob_t and return it.
+         TODO: record this mapping properly so that SIGBUS can
+        be handled.  */
+
+      blob = hb_blob_create (table->data, table->length,
+                            HB_MEMORY_MODE_READONLY,
+                            table, sfntfont_unmap_blob);
+
+      /* Note that sfntfont_unmap_blob will be called if the empty
+        blob is returned.  */
+      return blob;
+    }
+
+  xfree (table);
+#else /* !HAVE_MMAP */
+
+  /* Try to read the table conventionally.  */
+  info = data;
+#endif /* HAVE_MMAP */
+
+  data = sfnt_read_table (info->fd, info->directory, tag,
+                         &size);
+
+  if (!data)
+    return NULL;
+
+  return hb_blob_create (data, size, HB_MEMORY_MODE_WRITABLE,
+                        data, xfree);
+}
+
+/* Create or return a HarfBuzz font object corresponding to the
+   specified FONT.  Return the scale to convert between fwords and
+   pixels in POSITION_UNIT.  */
+
+hb_font_t *
+sfntfont_begin_hb_font (struct font *font, double *position_unit)
+{
+  struct sfnt_font_info *info;
+  hb_face_t *face;
+  int factor;
+
+  info = (struct sfnt_font_info *) font;
+
+  if (info->hb_font)
+    {
+      /* Calculate the scale factor.  */
+      *position_unit = 1.0 / 64.0;
+      return info->hb_font;
+    }
+
+  /* Create a face and then a font.  */
+  face = hb_face_create_for_tables (sfntfont_get_font_table, font,
+                                   NULL);
+
+  if (hb_face_get_glyph_count (face) > 0)
+    {
+      info->hb_font = hb_font_create (face);
+      if (!info->hb_font)
+       goto bail;
+
+      factor = font->pixel_size;
+
+      /* Set the scale and PPEM values.  */
+      hb_font_set_scale (info->hb_font, factor * 64, factor * 64);
+      hb_font_set_ppem (info->hb_font, factor, factor);
+
+      /* This is needed for HarfBuzz before 2.0.0; it is the default
+        in later versions.  */
+      hb_ot_font_set_funcs (info->hb_font);
+    }
+
+ bail:
+  hb_face_destroy (face);
+
+  /* Calculate the scale factor.  */
+  *position_unit = 1.0 / 64.0;
+  return info->hb_font;
+}
+
+#endif /* HAVE_HARFBUZZ */
+
+
+
 void
 syms_of_sfntfont (void)
 {
diff --git a/src/sfntfont.h b/src/sfntfont.h
index dc37883b4a9..df387512d0d 100644
--- a/src/sfntfont.h
+++ b/src/sfntfont.h
@@ -42,6 +42,7 @@ extern void sfntfont_close (struct font *);
 extern int sfntfont_draw (struct glyph_string *, int, int,
                          int, int, bool);
 extern Lisp_Object sfntfont_list_family (struct frame *);
+extern int sfntfont_get_variation_glyphs (struct font *, int, unsigned[256]);
 
 
 /* Initialization functions.  */
@@ -65,4 +66,14 @@ extern bool sfntfont_detect_sigbus (void *);
 
 #endif /* HAVE_MMAP */
 
+
+
+/* HarfBuzz specific functions.  */
+
+#ifdef HAVE_HARFBUZZ
+
+extern hb_font_t *sfntfont_begin_hb_font (struct font *, double *);
+
+#endif /* HAVE_HARFBUZZ */
+
 #endif /* _SFNTFONT_H_ */
diff --git a/src/textconv.c b/src/textconv.c
index a4e3116fb68..4fa92f43ecd 100644
--- a/src/textconv.c
+++ b/src/textconv.c
@@ -1723,12 +1723,12 @@ DEFUN ("set-text-conversion-style", 
Fset_text_conversion_style,
        Sset_text_conversion_style, 1, 1, 0,
        doc: /* Set the text conversion style in the current buffer.
 
-Set `text-conversion-mode' to VALUE, then force any input method
+Set `text-conversion-style' to VALUE, then force any input method
 editing frame displaying this buffer to stop itself.
 
 This can lead to a significant amount of time being taken by the input
 method resetting itself, so you should not use this function lightly;
-instead, set `text-conversion-mode' before your buffer is displayed,
+instead, set `text-conversion-style' before your buffer is displayed,
 and let redisplay manage the input method appropriately.  */)
   (Lisp_Object value)
 {



reply via email to

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