From 6bd1cfa24fd04de855e53e74b46cdf4047bced4c Mon Sep 17 00:00:00 2001 From: Po Lu Date: Mon, 20 Mar 2023 14:47:39 +0800 Subject: [PATCH] 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 #include +#include + #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 +#include +#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) { -- 2.39.2