[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)
{