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.
This commit is contained in:
Po Lu 2023-03-20 14:47:39 +08:00
parent 739558c369
commit 6bd1cfa24f
9 changed files with 1322 additions and 31 deletions

View file

@ -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

View file

@ -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,

View file

@ -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
(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))))
(or y-or-n-p-history-variable t)))))
(setq answer (if (member str '("y" "Y")) 'act 'skip)))))
(let ((ret (eq answer 'act)))
(unless noninteractive

View file

@ -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);
/* 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);

View file

@ -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 */

View file

@ -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,
.get_variation_glyphs = sfntfont_get_variation_glyphs,
/* TODO: list_family, shaping. */
#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 */
};

View file

@ -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,6 +2943,10 @@ sfntfont_draw (struct glyph_string *s, int from, int to,
/* Now work out where to put the outline. */
x_coords[i - from] = current_x;
if (s->padding_p)
current_x += 1;
else
current_x += SFNT_CEIL_FIXED (metrics.advance) >> 16;
}
@ -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)
{

View file

@ -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_ */

View file

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