mirror of
https://github.com/masscollaborationlabs/emacs.git
synced 2025-07-16 17:00:06 +00:00
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:
parent
739558c369
commit
6bd1cfa24f
9 changed files with 1322 additions and 31 deletions
|
@ -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
|
||||
|
|
16
java/INSTALL
16
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,
|
||||
|
|
14
lisp/subr.el
14
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
|
||||
(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
|
||||
|
|
739
src/sfnt.c
739
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);
|
||||
|
||||
/* 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);
|
||||
|
|
165
src/sfnt.h
165
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 */
|
||||
|
||||
|
||||
|
|
|
@ -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 */
|
||||
};
|
||||
|
||||
|
||||
|
|
382
src/sfntfont.c
382
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,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)
|
||||
{
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue