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;
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;
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
if (lseek (fd, offset, SEEK_SET) == (off_t) -1)
return (struct sfnt_cmap_encoding_subtable_data *) -1;
- if (read (fd, &header, sizeof header) < sizeof header)
+ if (read (fd, &header.format, sizeof header.format)
+ < sizeof header.format)
return (struct sfnt_cmap_encoding_subtable_data *) -1;
sfnt_swap16 (&header.format);
- sfnt_swap16 (&header.length);
+
+ /* Format 14 tables are rather special: they do not have a 16-bit
+ `length' field. When these tables are encountered, leave reading
+ the rest of the header to `sfnt_read_cmap_table_14'. */
+
+ if (header.format != 14)
+ {
+ if (read (fd, &header.length, sizeof header.length)
+ < sizeof header.length)
+ return (struct sfnt_cmap_encoding_subtable_data *) -1;
+
+ sfnt_swap16 (&header.length);
+ }
+ else
+ header.length = 0;
switch (header.format)
{
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;
}
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)
{
/* 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,
\f
+/* 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;
+}
+
+\f
+
+#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 */
+
+\f
+
+#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 */
+
+\f
+
#ifdef TEST
struct sfnt_test_dcontext
\f
+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);
+ }
+}
+
+\f
+
/* Main entry point. */
/* Simple tests that were used while developing this file. By the
struct sfnt_raster **rasters;
size_t length;
- if (argc != 2)
+ if (argc < 2)
return 1;
if (!strcmp (argv[1], "--check-interpreter"))
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);
#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
/* 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. */
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];
}
}
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];
}
}
/* Now pick the best character map. */
*cmap = sfntfont_select_cmap (table, subtables, data,
- subtable);
+ subtable, NULL);
/* Free the cmap data. */
/* 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;
/* 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
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));
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;
#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);
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]);
}
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. */
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:
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);
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. */
info->fpgm = NULL;
info->cvt = NULL;
info->interpreter = NULL;
+ info->uvs = NULL;
#ifdef HAVE_MMAP
#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);
}
/* Now work out where to put the outline. */
x_coords[i - from] = current_x;
- current_x += SFNT_CEIL_FIXED (metrics.advance) >> 16;
+
+ if (s->padding_p)
+ current_x += 1;
+ else
+ current_x += SFNT_CEIL_FIXED (metrics.advance) >> 16;
}
/* Call the window system function to put the glyphs to the
\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;
+}
+
+\f
+
/* mmap specific stuff. */
#ifdef HAVE_MMAP
\f
+/* 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 */
+
+\f
+
void
syms_of_sfntfont (void)
{