emacs/src/sfnt.c

21488 lines
528 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* TrueType format font support for GNU Emacs.
Copyright (C) 2023-2024 Free Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.
GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <config.h>
#include "sfnt.h"
#include <assert.h>
#include <attribute.h>
#include <byteswap.h>
#include <fcntl.h>
#include <intprops.h>
#include <inttypes.h>
#include <stdbit.h>
#include <stdckdint.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <setjmp.h>
#include <errno.h>
#include <alloca.h>
#ifdef HAVE_MMAP
#include <sys/mman.h>
#endif
#if defined __GNUC__ && !defined __clang__
#pragma GCC diagnostic ignored "-Wstringop-overflow"
#endif
#ifdef TEST
#include <time.h>
#include <timespec.h>
#include <sys/wait.h>
#include <errno.h>
#include <X11/Xlib.h>
#include <X11/extensions/Xrender.h>
static void *
xmalloc (size_t size)
{
void *ptr;
ptr = malloc (size);
if (!ptr)
abort ();
return ptr;
}
MAYBE_UNUSED static void *
xzalloc (size_t size)
{
void *ptr;
ptr = calloc (1, size);
if (!ptr)
abort ();
return ptr;
}
static void *
xrealloc (void *ptr, size_t size)
{
void *new_ptr;
new_ptr = realloc (ptr, size);
if (!new_ptr)
abort ();
return new_ptr;
}
static void
xfree (void *ptr)
{
free (ptr);
}
/* Use this for functions that are static while building in test mode,
but are used outside as well. */
#define TEST_STATIC static
/* Needed for tests. */
#define ARRAYELTS(arr) (sizeof (arr) / sizeof (arr)[0])
/* Also necessary. */
#define AVOID _Noreturn ATTRIBUTE_COLD void
#else
#define TEST_STATIC
#include "lisp.h"
#endif
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
/* This file provides generic support for reading most TrueType fonts,
and some OpenType fonts with TrueType outlines, along with glyph
lookup, outline decomposition, and alpha mask generation from those
glyphs. It is intended to be used on any platform where proper
libraries such as FreeType are not easily available, and the native
font library is too limited for Emacs to support properly.
Unlike most popular libraries for handling fonts, no ``font'' or
``face'' type is provided. Instead, routines and structure
definitions for accessing and making use of individual tables in a
font file are exported, which allows for flexibility in the rest of
Emacs.
Try not to keep this file too dependent on Emacs. Everything Lisp
related goes in sfntfont.c. The author wants to keep using it for
some other (free) software.
The source of reference is the TrueType Reference Manual, published
by Apple Computer, which is currently found at:
https://developer.apple.com/fonts/TrueType-Reference-Manual/
Apple's TrueType implementation is notably missing features
provided by Microsoft's extended OpenType scaler, such as the two
additional phantom points on the Y axis, and also behaves
differently, especially when it comes to considering phantom points
as anchors in compound glyphs.
As a result, do not expect this scaler to work well with Microsoft
fonts such as Arial. */
/* Mapping between sfnt table names and their identifiers. */
static uint32_t sfnt_table_names[] =
{
[SFNT_TABLE_CMAP] = 0x636d6170,
[SFNT_TABLE_GLYF] = 0x676c7966,
[SFNT_TABLE_HEAD] = 0x68656164,
[SFNT_TABLE_HHEA] = 0x68686561,
[SFNT_TABLE_HMTX] = 0x686d7478,
[SFNT_TABLE_LOCA] = 0x6c6f6361,
[SFNT_TABLE_MAXP] = 0x6d617870,
[SFNT_TABLE_NAME] = 0x6e616d65,
[SFNT_TABLE_META] = 0x6d657461,
[SFNT_TABLE_CVT ] = 0x63767420,
[SFNT_TABLE_FPGM] = 0x6670676d,
[SFNT_TABLE_PREP] = 0x70726570,
[SFNT_TABLE_FVAR] = 0x66766172,
[SFNT_TABLE_GVAR] = 0x67766172,
[SFNT_TABLE_CVAR] = 0x63766172,
[SFNT_TABLE_AVAR] = 0x61766172,
[SFNT_TABLE_OS_2] = 0x4f532f32,
[SFNT_TABLE_POST] = 0x706f7374,
};
/* Swap values from TrueType to system byte order. */
static void
sfnt_swap16_1 (uint16_t *value)
{
#ifndef WORDS_BIGENDIAN
*value = bswap_16 (*value);
#endif
}
static void
sfnt_swap32_1 (uint32_t *value)
{
#ifndef WORDS_BIGENDIAN
*value = bswap_32 (*value);
#endif
}
#define sfnt_swap16(what) (sfnt_swap16_1 ((uint16_t *) (what)))
#define sfnt_swap32(what) (sfnt_swap32_1 ((uint32_t *) (what)))
/* Read the table directory from the file FD. FD must currently be at
the start of the file (or an offset defined in the TTC header, if
applicable), and must be seekable. Return the table directory upon
success, else NULL.
Value is NULL upon failure, and the offset subtable upon success.
If FD is actually a TrueType collection file, value is -1. */
TEST_STATIC struct sfnt_offset_subtable *
sfnt_read_table_directory (int fd)
{
struct sfnt_offset_subtable *subtable;
ssize_t rc;
size_t offset, subtable_size;
int i;
subtable = xmalloc (sizeof *subtable);
offset = SFNT_ENDOF (struct sfnt_offset_subtable,
range_shift, uint16_t);
rc = read (fd, subtable, offset);
if (rc == -1 || rc < offset)
{
if (rc != -1 && rc >= sizeof (uint32_t))
{
/* Detect a TTC file. In that case, the first long will be
``ttcf''. */
sfnt_swap32 (&subtable->scaler_type);
if (subtable->scaler_type == SFNT_TTC_TTCF)
{
xfree (subtable);
return (struct sfnt_offset_subtable *) -1;
}
}
xfree (subtable);
return NULL;
}
sfnt_swap32 (&subtable->scaler_type);
/* Bail out early if this font is actually a TrueType collection
file. */
if (subtable->scaler_type == SFNT_TTC_TTCF)
{
xfree (subtable);
return (struct sfnt_offset_subtable *) -1;
}
sfnt_swap16 (&subtable->num_tables);
sfnt_swap16 (&subtable->search_range);
sfnt_swap16 (&subtable->entry_selector);
sfnt_swap16 (&subtable->range_shift);
/* Figure out how many more tables have to be read, and read each
one of them. */
subtable_size = (subtable->num_tables
* sizeof (struct sfnt_table_directory));
subtable = xrealloc (subtable, sizeof *subtable + subtable_size);
subtable->subtables
= (struct sfnt_table_directory *) (subtable + 1);
rc = read (fd, subtable->subtables, subtable_size);
if (rc == -1 || rc < offset)
{
xfree (subtable);
return NULL;
}
/* Swap each of the subtables. */
for (i = 0; i < subtable->num_tables; ++i)
{
sfnt_swap32 (&subtable->subtables[i].tag);
sfnt_swap32 (&subtable->subtables[i].checksum);
sfnt_swap32 (&subtable->subtables[i].offset);
sfnt_swap32 (&subtable->subtables[i].length);
}
return subtable;
}
/* Return a pointer to the table directory entry for TABLE in
SUBTABLE, or NULL if it was not found. */
static struct sfnt_table_directory *
sfnt_find_table (struct sfnt_offset_subtable *subtable,
enum sfnt_table table)
{
int i;
for (i = 0; i < subtable->num_tables; ++i)
{
if (subtable->subtables[i].tag == sfnt_table_names[table])
return &subtable->subtables[i];
}
return NULL;
}
/* Character mapping routines. */
/* Read a format 0 cmap subtable from FD. HEADER has already been
read. */
static struct sfnt_cmap_format_0 *
sfnt_read_cmap_format_0 (int fd,
struct sfnt_cmap_encoding_subtable_data *header)
{
struct sfnt_cmap_format_0 *format0;
ssize_t rc;
size_t wanted_size;
format0 = xmalloc (sizeof *format0);
/* Fill in fields that have already been read. */
format0->format = header->format;
format0->length = header->length;
/* Read the rest. */
wanted_size = (sizeof *format0
- offsetof (struct sfnt_cmap_format_0,
language));
rc = read (fd, &format0->language, wanted_size);
if (rc == -1 || rc < wanted_size)
{
xfree (format0);
return (struct sfnt_cmap_format_0 *) -1;
}
/* Swap fields and return. */
sfnt_swap16 (&format0->language);
return format0;
}
/* Read a format 2 cmap subtable from FD. HEADER has already been
read. */
static struct sfnt_cmap_format_2 *
sfnt_read_cmap_format_2 (int fd,
struct sfnt_cmap_encoding_subtable_data *header)
{
struct sfnt_cmap_format_2 *format2;
ssize_t rc;
size_t min_bytes;
int i, nsub;
/* Reject contents that are too small. */
min_bytes = SFNT_ENDOF (struct sfnt_cmap_format_2,
sub_header_keys, uint16_t[256]);
if (header->length < min_bytes)
return NULL;
/* Add enough bytes at the end to fit the two variable length
pointers. */
format2 = xmalloc (header->length + sizeof *format2);
format2->format = header->format;
format2->length = header->length;
/* Read the part before the variable length data. */
min_bytes -= offsetof (struct sfnt_cmap_format_2, language);
rc = read (fd, &format2->language, min_bytes);
if (rc == -1 || rc < min_bytes)
{
xfree (format2);
return (struct sfnt_cmap_format_2 *) -1;
}
/* Swap the fields now. */
sfnt_swap16 (&format2->language);
/* At the same time, look for the largest value in sub_header_keys.
That will be the number of subheaders and elements in the glyph
index array. */
nsub = 0;
for (i = 0; i < 256; ++i)
{
sfnt_swap16 (&format2->sub_header_keys[i]);
if (format2->sub_header_keys[i] > nsub)
nsub = format2->sub_header_keys[i];
}
if (!nsub)
/* If there are no subheaders, then things are finished. */
return format2;
/* Otherwise, read the rest of the variable length data to the end
of format2. */
min_bytes = (format2->length
- SFNT_ENDOF (struct sfnt_cmap_format_2,
sub_header_keys, uint16_t[256]));
rc = read (fd, format2 + 1, min_bytes);
if (rc == -1 || rc < min_bytes)
{
xfree (format2);
return (struct sfnt_cmap_format_2 *) -1;
}
/* Check whether or not the data is of the correct size. */
if (min_bytes < nsub * sizeof *format2->subheaders)
{
xfree (format2);
return (struct sfnt_cmap_format_2 *) -1;
}
/* Point the data pointers to the right location, swap everything,
and return. */
format2->subheaders
= (struct sfnt_cmap_format_2_subheader *) (format2 + 1);
format2->glyph_index_array
= (uint16_t *) (format2->subheaders + nsub);
for (i = 0; i < nsub; ++i)
{
sfnt_swap16 (&format2->subheaders[i].first_code);
sfnt_swap16 (&format2->subheaders[i].entry_count);
sfnt_swap16 (&format2->subheaders[i].id_delta);
sfnt_swap16 (&format2->subheaders[i].id_range_offset);
}
/* Figure out how big the glyph index array is, and swap everything
there. */
format2->num_glyphs
= (min_bytes - nsub * sizeof *format2->subheaders) / 2;
for (i = 0; i < format2->num_glyphs; ++i)
sfnt_swap16 (&format2->glyph_index_array[i]);
return format2;
}
/* Read a format 4 cmap subtable from FD. HEADER has already been
read. */
static struct sfnt_cmap_format_4 *
sfnt_read_cmap_format_4 (int fd,
struct sfnt_cmap_encoding_subtable_data *header)
{
struct sfnt_cmap_format_4 *format4;
size_t min_bytes, variable_size;
ssize_t rc;
size_t bytes_minus_format4;
int seg_count, i;
min_bytes = SFNT_ENDOF (struct sfnt_cmap_format_4,
range_shift, uint16_t);
/* Check that the length is at least min_bytes. */
if (header->length < min_bytes)
return NULL;
/* Allocate the format4 buffer, making it the size of the buffer
itself plus that of the data. */
format4 = xmalloc (header->length + sizeof *format4);
/* Copy over fields that have already been read. */
format4->format = header->format;
format4->length = header->length;
/* Read the initial data. */
min_bytes -= offsetof (struct sfnt_cmap_format_4, language);
rc = read (fd, &format4->language, min_bytes);
if (rc == -1 || rc < min_bytes)
{
xfree (format4);
return (struct sfnt_cmap_format_4 *) -1;
}
/* Swap fields that have been read. */
sfnt_swap16 (&format4->language);
sfnt_swap16 (&format4->seg_count_x2);
sfnt_swap16 (&format4->search_range);
sfnt_swap16 (&format4->entry_selector);
sfnt_swap16 (&format4->range_shift);
/* Get the number of segments to read. */
seg_count = format4->seg_count_x2 / 2;
/* Now calculate whether or not the size is sufficiently large. */
bytes_minus_format4
= format4->length - SFNT_ENDOF (struct sfnt_cmap_format_4,
range_shift, uint16_t);
variable_size = (seg_count * sizeof *format4->end_code
+ sizeof *format4->reserved_pad
+ seg_count * sizeof *format4->start_code
+ seg_count * sizeof *format4->id_delta
+ seg_count * sizeof *format4->id_range_offset);
if (bytes_minus_format4 < variable_size)
{
/* Not enough bytes to fit the entire implied table
contents. */
xfree (format4);
return NULL;
}
/* Read the rest of the bytes to the end of format4. */
rc = read (fd, format4 + 1, bytes_minus_format4);
if (rc == -1 || rc < bytes_minus_format4)
{
xfree (format4);
return (struct sfnt_cmap_format_4 *) -1;
}
/* Set data pointers to the right locations. */
format4->end_code = (uint16_t *) (format4 + 1);
format4->reserved_pad = format4->end_code + seg_count;
format4->start_code = format4->reserved_pad + 1;
format4->id_delta = (int16_t *) (format4->start_code + seg_count);
format4->id_range_offset = format4->id_delta + seg_count;
format4->glyph_index_array = (uint16_t *) (format4->id_range_offset
+ seg_count);
/* N.B. that the number of elements in glyph_index_array is
(bytes_minus_format4 - variable_size) / 2. Swap all the
data. */
sfnt_swap16 (format4->reserved_pad);
for (i = 0; i < seg_count; ++i)
{
sfnt_swap16 (&format4->end_code[i]);
sfnt_swap16 (&format4->start_code[i]);
sfnt_swap16 (&format4->id_delta[i]);
sfnt_swap16 (&format4->id_range_offset[i]);
}
format4->glyph_index_size
= (bytes_minus_format4 - variable_size) / 2;
for (i = 0; i < format4->glyph_index_size; ++i)
sfnt_swap16 (&format4->glyph_index_array[i]);
/* Done. Return the format 4 character map. */
return format4;
}
/* Read a format 6 cmap subtable from FD. HEADER has already been
read. */
static struct sfnt_cmap_format_6 *
sfnt_read_cmap_format_6 (int fd,
struct sfnt_cmap_encoding_subtable_data *header)
{
struct sfnt_cmap_format_6 *format6;
size_t min_size;
ssize_t rc;
uint16_t i;
min_size = SFNT_ENDOF (struct sfnt_cmap_format_6, entry_count,
uint16_t);
/* See if header->length is big enough. */
if (header->length < min_size)
return NULL;
/* Allocate the buffer to hold header->size and enough for at least
the glyph index array pointer. */
format6 = xmalloc (header->length + sizeof *format6);
/* Fill in data that has already been read. */
format6->format = header->format;
format6->length = header->length;
/* Read the fixed size data. */
min_size -= offsetof (struct sfnt_cmap_format_6, language);
rc = read (fd, &format6->language, min_size);
if (rc == -1 || rc < min_size)
{
xfree (format6);
return (struct sfnt_cmap_format_6 *) -1;
}
/* Swap what was read. */
sfnt_swap16 (&format6->language);
sfnt_swap16 (&format6->first_code);
sfnt_swap16 (&format6->entry_count);
/* Figure out whether or not header->length is sufficient to hold
the variable length data. */
if (header->length
< format6->entry_count * sizeof *format6->glyph_index_array)
{
xfree (format6);
return NULL;
}
/* Read the variable length data. */
rc = read (fd, format6 + 1,
(format6->entry_count
* sizeof *format6->glyph_index_array));
if (rc == -1 || (rc < (format6->entry_count
* sizeof *format6->glyph_index_array)))
{
xfree (format6);
return (struct sfnt_cmap_format_6 *) -1;
}
/* Set the data pointer and swap everything. */
format6->glyph_index_array = (uint16_t *) (format6 + 1);
for (i = 0; i < format6->entry_count; ++i)
sfnt_swap16 (&format6->glyph_index_array[i]);
/* All done! */
return format6;
}
/* Read a format 8 cmap subtable from FD. HEADER has already been
read. */
static struct sfnt_cmap_format_8 *
sfnt_read_cmap_format_8 (int fd,
struct sfnt_cmap_encoding_subtable_data *header)
{
struct sfnt_cmap_format_8 *format8;
size_t min_size, temp;
ssize_t rc;
uint32_t length, i;
/* Read the 32-bit length field. */
if (read (fd, &length, sizeof length) < (int) sizeof length)
return (struct sfnt_cmap_format_8 *) -1;
/* Swap the 32-bit length field. */
sfnt_swap32 (&length);
min_size = SFNT_ENDOF (struct sfnt_cmap_format_8, num_groups,
uint32_t);
/* Make sure the header is at least as large as min_size. */
if (length < min_size)
return NULL;
/* Allocate a buffer of sufficient size. */
format8 = xmalloc (length + sizeof *format8);
format8->format = header->format;
format8->reserved = header->length;
format8->length = length;
/* Read the fixed length data. */
min_size -= offsetof (struct sfnt_cmap_format_8, language);
rc = read (fd, &format8->language, min_size);
if (rc == -1 || rc < min_size)
{
xfree (format8);
return (struct sfnt_cmap_format_8 *) -1;
}
/* Swap what was read. */
sfnt_swap32 (&format8->language);
sfnt_swap32 (&format8->num_groups);
/* See if the size is sufficient to read the variable length
data. */
min_size = SFNT_ENDOF (struct sfnt_cmap_format_8, num_groups,
uint32_t);
if (ckd_mul (&temp, format8->num_groups, sizeof *format8->groups))
{
xfree (format8);
return NULL;
}
if (ckd_add (&min_size, min_size, temp))
{
xfree (format8);
return NULL;
}
if (length < min_size)
{
xfree (format8);
return NULL;
}
/* Now read the variable length data. */
rc = read (fd, format8 + 1, temp);
if (rc == -1 || rc < temp)
{
xfree (format8);
return (struct sfnt_cmap_format_8 *) -1;
}
/* Set the pointer to the variable length data. */
format8->groups
= (struct sfnt_cmap_format_8_or_12_group *) (format8 + 1);
for (i = 0; i < format8->num_groups; ++i)
{
sfnt_swap32 (&format8->groups[i].start_char_code);
sfnt_swap32 (&format8->groups[i].end_char_code);
sfnt_swap32 (&format8->groups[i].start_glyph_code);
}
/* All done. */
return format8;
}
/* Read a format 12 cmap subtable from FD. HEADER has already been
read. */
static struct sfnt_cmap_format_12 *
sfnt_read_cmap_format_12 (int fd,
struct sfnt_cmap_encoding_subtable_data *header)
{
struct sfnt_cmap_format_12 *format12;
size_t min_size, temp;
ssize_t rc;
uint32_t length, i;
/* Read the 32-bit length field. */
if (read (fd, &length, sizeof length) < (int) sizeof length)
return (struct sfnt_cmap_format_12 *) -1;
/* Swap the 32-bit length field. */
sfnt_swap32 (&length);
min_size = SFNT_ENDOF (struct sfnt_cmap_format_12, num_groups,
uint32_t);
/* Make sure the header is at least as large as min_size. */
if (length < min_size)
return NULL;
/* Allocate a buffer of sufficient size. */
format12 = xmalloc (length + sizeof *format12);
format12->format = header->format;
format12->reserved = header->length;
format12->length = length;
/* Read the fixed length data. */
min_size -= offsetof (struct sfnt_cmap_format_12, language);
rc = read (fd, &format12->language, min_size);
if (rc == -1 || rc < min_size)
{
xfree (format12);
return (struct sfnt_cmap_format_12 *) -1;
}
/* Swap what was read. */
sfnt_swap32 (&format12->language);
sfnt_swap32 (&format12->num_groups);
/* See if the size is sufficient to read the variable length
data. */
min_size = SFNT_ENDOF (struct sfnt_cmap_format_12, num_groups,
uint32_t);
if (ckd_mul (&temp, format12->num_groups, sizeof *format12->groups))
{
xfree (format12);
return NULL;
}
if (ckd_add (&min_size, min_size, temp))
{
xfree (format12);
return NULL;
}
if (length < min_size)
{
xfree (format12);
return NULL;
}
/* Now read the variable length data. */
rc = read (fd, format12 + 1, temp);
if (rc == -1 || rc < temp)
{
xfree (format12);
return (struct sfnt_cmap_format_12 *) -1;
}
/* Set the pointer to the variable length data. */
format12->groups
= (struct sfnt_cmap_format_8_or_12_group *) (format12 + 1);
for (i = 0; i < format12->num_groups; ++i)
{
sfnt_swap32 (&format12->groups[i].start_char_code);
sfnt_swap32 (&format12->groups[i].end_char_code);
sfnt_swap32 (&format12->groups[i].start_glyph_code);
}
/* All done. */
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];
uint32_t i;
/* Read the length field and number of variation selector
records. */
if (read (fd, buffer1, sizeof buffer1) < (int) 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 (ckd_mul (&temp, num_records, sizeof *format14->records)
|| ckd_add (&size, size, temp))
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) < (int) 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
upon an IO error. */
static struct sfnt_cmap_encoding_subtable_data *
sfnt_read_cmap_table_1 (int fd, uint32_t directory_offset,
uint32_t table_offset)
{
off_t offset;
struct sfnt_cmap_encoding_subtable_data header;
if (ckd_add (&offset, directory_offset, table_offset))
return (struct sfnt_cmap_encoding_subtable_data *) -1;
if (lseek (fd, offset, SEEK_SET) == (off_t) -1)
return (struct sfnt_cmap_encoding_subtable_data *) -1;
if (read (fd, &header.format, sizeof header.format)
< (int) 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)
< (int) sizeof header.length)
return (struct sfnt_cmap_encoding_subtable_data *) -1;
sfnt_swap16 (&header.length);
}
else
header.length = 0;
switch (header.format)
{
case 0:
/* If the length changes, then something has changed to the
format. */
if (header.length != 262)
return NULL;
return ((struct sfnt_cmap_encoding_subtable_data *)
sfnt_read_cmap_format_0 (fd, &header));
case 2:
return ((struct sfnt_cmap_encoding_subtable_data *)
sfnt_read_cmap_format_2 (fd, &header));
case 4:
return ((struct sfnt_cmap_encoding_subtable_data *)
sfnt_read_cmap_format_4 (fd, &header));
case 6:
return ((struct sfnt_cmap_encoding_subtable_data *)
sfnt_read_cmap_format_6 (fd, &header));
case 8:
return ((struct sfnt_cmap_encoding_subtable_data *)
sfnt_read_cmap_format_8 (fd, &header));
case 12:
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;
}
}
/* Read the CMAP table of a given font from the file FD. Use the
table directory specified in SUBTABLE.
Return the CMAP table and a list of encoding subtables in
*SUBTABLES and *DATA upon success, else NULL. If DATA is NULL, do
not read the subtable data. */
TEST_STATIC struct sfnt_cmap_table *
sfnt_read_cmap_table (int fd, struct sfnt_offset_subtable *subtable,
struct sfnt_cmap_encoding_subtable **subtables,
struct sfnt_cmap_encoding_subtable_data ***data)
{
struct sfnt_table_directory *directory;
struct sfnt_cmap_table *cmap;
ssize_t rc;
int i, j;
/* Find the CMAP table in the table directory. */
directory = sfnt_find_table (subtable, SFNT_TABLE_CMAP);
if (!directory)
return NULL;
/* Seek to the start of the CMAP table. */
if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
return NULL;
/* Read the table header. */
cmap = xmalloc (sizeof *cmap);
rc = read (fd, cmap, sizeof *cmap);
if (rc < (int) sizeof *cmap)
{
xfree (cmap);
return NULL;
}
/* Swap the header data. */
sfnt_swap16 (&cmap->version);
sfnt_swap16 (&cmap->num_subtables);
if (cmap->version != 0)
{
xfree (cmap);
return NULL;
}
*subtables = xmalloc (cmap->num_subtables
* sizeof **subtables);
/* First, read the common parts of each encoding subtable. */
for (i = 0; i < cmap->num_subtables; ++i)
{
/* Read the common part of the new subtable. */
rc = read (fd, &(*subtables)[i], sizeof (*subtables)[i]);
if (rc < (int) sizeof (*subtables)[i])
{
xfree (cmap);
xfree (*subtables);
return NULL;
}
sfnt_swap16 (&(*subtables)[i].platform_id);
sfnt_swap16 (&(*subtables)[i].platform_specific_id);
sfnt_swap32 (&(*subtables)[i].offset);
}
/* If data is NULL, the caller only wants the table headers. */
if (!data)
return cmap;
/* Second, read each encoding subtable itself. */
*data = xmalloc (cmap->num_subtables * sizeof *data);
for (i = 0; i < cmap->num_subtables; ++i)
{
(*data)[i] = sfnt_read_cmap_table_1 (fd, directory->offset,
(*subtables)[i].offset);
if ((*data)[i] == (void *) -1)
{
/* An IO error occurred (as opposed to the subtable format
being unsupported.) Return now. */
for (j = 0; j < i; ++j)
xfree ((*data)[j]);
xfree (*data);
xfree (*subtables);
xfree (cmap);
return NULL;
}
}
return cmap;
}
/* Look up the glyph corresponding to CHARACTER in the format 0 cmap
FORMAT0. Return 0 if no glyph was found. */
static sfnt_glyph
sfnt_lookup_glyph_0 (sfnt_char character,
struct sfnt_cmap_format_0 *format0)
{
if (character >= 256)
return 0;
return format0->glyph_index_array[character];
}
/* Look up the glyph corresponding to CHARACTER in the format 2 cmap
FORMAT2. Return 0 if no glyph was found. */
static sfnt_glyph
sfnt_lookup_glyph_2 (sfnt_char character,
struct sfnt_cmap_format_2 *format2)
{
unsigned char i, k, j;
struct sfnt_cmap_format_2_subheader *subheader;
unsigned char *slice;
uint16_t glyph;
if (character > 65335)
return 0;
i = character >> 16;
j = character & 0xff;
k = format2->sub_header_keys[i] / 8;
if (k)
{
subheader = &format2->subheaders[k];
if (subheader->first_code <= j
&& j <= ((int) subheader->first_code
+ (int) subheader->entry_count))
{
/* id_range_offset is actually the number of bytes past
itself containing the uint16_t ``slice''. It is possibly
unaligned. */
slice = (unsigned char *) &subheader->id_range_offset;
slice += subheader->id_range_offset;
slice += (j - subheader->first_code) * sizeof (uint16_t);
if (slice < (unsigned char *) format2->glyph_index_array
|| (slice + 1
> (unsigned char *) (format2->glyph_index_array
+ format2->num_glyphs)))
/* The character is out of bounds. */
return 0;
memcpy (&glyph, slice, sizeof glyph);
return (glyph + subheader->id_delta) % 65536;
}
else
return 0;
}
/* k is 0, so glyph_index_array[i] is the glyph. */
return (i < format2->num_glyphs
? format2->glyph_index_array[i]
: 0);
}
/* Like `bsearch', but return the element ordered exactly above KEY if
one exists and KEY itself cannot be located. */
static void *
sfnt_bsearch_above (const void *key, const void *base,
size_t nmemb, size_t size,
int (*compar) (const void *,
const void *))
{
const unsigned char *bytes, *sample;
size_t low, high, mid;
bytes = base;
low = 0;
high = nmemb - 1;
if (!nmemb)
return NULL;
while (low != high)
{
mid = low + (high - low) / 2;
sample = bytes + mid * size;
if ((*compar) (key, sample) > 0)
low = mid + 1;
else
high = mid;
}
sample = bytes + low * size;
if (low == nmemb - 1
&& (*compar) (key, sample) > 0)
return NULL;
return (unsigned char *) bytes + low * size;
}
/* Compare two uint16_t's. Used to bisect through a format 4
table. */
static int
sfnt_compare_uint16 (const void *a, const void *b)
{
return ((int) *((uint16_t *) a)) - ((int) *((uint16_t *) b));
}
/* Look up the glyph corresponding to CODE in the format 4 cmap
FORMAT4, using the table segment SEGMENT. Value is 0 if no glyph
was found. */
static sfnt_glyph
sfnt_lookup_glyph_4_1 (uint16_t code, uint16_t segment,
struct sfnt_cmap_format_4 *format4)
{
uint16_t *index;
if (format4->id_range_offset[segment])
{
/* id_range_offset is not 0, so the glyph mapping depends on
it. */
index = (uint16_t *) (&format4->id_range_offset[segment]
+ format4->id_range_offset[segment] / 2
+ (code - format4->start_code[segment]));
/* Check that index is not out of bounds. */
if (index >= (format4->glyph_index_array
+ format4->glyph_index_size)
|| index < format4->glyph_index_array)
return 0;
/* Return what is in index. */
return (*index ? (format4->id_delta[segment]
+ *index) % 65536 : 0);
}
/* Otherwise, just add id_delta. */
return (format4->id_delta[segment] + code) % 65536;
}
/* Look up the glyph corresponding to CHARACTER in the format 4 cmap
FORMAT4. Return 0 if no glyph was found. */
static sfnt_glyph
sfnt_lookup_glyph_4 (sfnt_char character,
struct sfnt_cmap_format_4 *format4)
{
uint16_t *segment_address;
uint16_t code, segment;
sfnt_glyph glyph;
if (character > 65535)
return 0;
code = character;
/* Find the segment ending above or at CHARACTER. */
segment_address = sfnt_bsearch_above (&code, format4->end_code,
format4->seg_count_x2 / 2,
sizeof code,
sfnt_compare_uint16);
segment = segment_address - format4->end_code;
/* If the segment starts too late, return 0. */
if (!segment_address || format4->start_code[segment] > character)
return 0;
glyph = sfnt_lookup_glyph_4_1 (character, segment, format4);
if (glyph)
return glyph;
/* Fail. */
return 0;
}
/* Look up the glyph corresponding to CHARACTER in the format 6 cmap
FORMAT6. Return 0 if no glyph was found. */
static sfnt_glyph
sfnt_lookup_glyph_6 (sfnt_char character,
struct sfnt_cmap_format_6 *format6)
{
if (character < format6->first_code
|| character >= (format6->first_code
+ (int) format6->entry_count))
return 0;
return format6->glyph_index_array[character - format6->first_code];
}
/* Compare the sfnt_char A with B's end code. Employed to bisect
through a format 8 or 12 table. */
static int
sfnt_compare_char (const void *a, const void *b)
{
struct sfnt_cmap_format_8_or_12_group *group;
group = (struct sfnt_cmap_format_8_or_12_group *) b;
return ((int) *((sfnt_char *) a)) - group->end_char_code;
}
/* Look up the glyph corresponding to CHARACTER in the format 8 cmap
FORMAT8. Return 0 if no glyph was found. */
static sfnt_glyph
sfnt_lookup_glyph_8 (sfnt_char character,
struct sfnt_cmap_format_8 *format8)
{
uint32_t i;
struct sfnt_cmap_format_8_or_12_group *group;
if (character > 0xffffffff)
return 0;
if (format8->num_groups > 64)
{
/* This table is large, likely supplied by a CJK or similar
font. Perform a binary search. */
/* Find the group whose END_CHAR_CODE is greater than or equal
to CHARACTER. */
group = sfnt_bsearch_above (&character, format8->groups,
format8->num_groups,
sizeof format8->groups[0],
sfnt_compare_char);
if (!group || group->start_char_code > character)
/* No glyph matches this group. */
return 0;
/* Otherwise, use this group to map the character to a
glyph. */
return (group->start_glyph_code
+ character
- group->start_char_code);
}
for (i = 0; i < format8->num_groups; ++i)
{
if (format8->groups[i].start_char_code <= character
&& format8->groups[i].end_char_code >= character)
return (format8->groups[i].start_glyph_code
+ (character
- format8->groups[i].start_char_code));
}
return 0;
}
/* Look up the glyph corresponding to CHARACTER in the format 12 cmap
FORMAT12. Return 0 if no glyph was found. */
static sfnt_glyph
sfnt_lookup_glyph_12 (sfnt_char character,
struct sfnt_cmap_format_12 *format12)
{
uint32_t i;
struct sfnt_cmap_format_8_or_12_group *group;
if (character > 0xffffffff)
return 0;
if (format12->num_groups > 64)
{
/* This table is large, likely supplied by a CJK or similar
font. Perform a binary search. */
/* Find the group whose END_CHAR_CODE is greater than or equal
to CHARACTER. */
group = sfnt_bsearch_above (&character, format12->groups,
format12->num_groups,
sizeof format12->groups[0],
sfnt_compare_char);
if (!group || group->start_char_code > character)
/* No glyph matches this group. */
return 0;
/* Otherwise, use this group to map the character to a
glyph. */
return (group->start_glyph_code
+ character
- group->start_char_code);
}
for (i = 0; i < format12->num_groups; ++i)
{
if (format12->groups[i].start_char_code <= character
&& format12->groups[i].end_char_code >= character)
return (format12->groups[i].start_glyph_code
+ (character
- format12->groups[i].start_char_code));
}
return 0;
}
/* 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.
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,
struct sfnt_cmap_encoding_subtable_data *data)
{
switch (data->format)
{
case 0:
return sfnt_lookup_glyph_0 (character,
(struct sfnt_cmap_format_0 *) data);
case 2:
return sfnt_lookup_glyph_2 (character,
(struct sfnt_cmap_format_2 *) data);
case 4:
return sfnt_lookup_glyph_4 (character,
(struct sfnt_cmap_format_4 *) data);
case 6:
return sfnt_lookup_glyph_6 (character,
(struct sfnt_cmap_format_6 *) data);
case 8:
return sfnt_lookup_glyph_8 (character,
(struct sfnt_cmap_format_8 *) data);
case 12:
return sfnt_lookup_glyph_12 (character,
(struct sfnt_cmap_format_12 *) data);
}
return 0;
}
/* Header reading routines. */
/* Read the head table of a given font FD. Use the table directory
specified in SUBTABLE.
Return the head table upon success, else NULL. */
TEST_STATIC struct sfnt_head_table *
sfnt_read_head_table (int fd, struct sfnt_offset_subtable *subtable)
{
struct sfnt_table_directory *directory;
struct sfnt_head_table *head;
ssize_t rc;
/* Find the table in the directory. */
directory = sfnt_find_table (subtable, SFNT_TABLE_HEAD);
if (!directory)
return NULL;
/* Seek to the location given in the directory. */
if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
return NULL;
/* Read the entire table. */
head = xmalloc (sizeof *head);
rc = read (fd, head, sizeof *head);
if (rc < (int) sizeof *head)
{
xfree (head);
return NULL;
}
/* Swap the header data. */
sfnt_swap32 (&head->version);
sfnt_swap32 (&head->revision);
if (head->version != 0x00010000)
{
xfree (head);
return NULL;
}
/* Swap the rest of the data. */
sfnt_swap32 (&head->checksum_adjustment);
sfnt_swap32 (&head->magic);
if (head->magic != 0x5f0f3cf5)
{
xfree (head);
return NULL;
}
sfnt_swap16 (&head->flags);
sfnt_swap16 (&head->units_per_em);
sfnt_swap32 (&head->created_high);
sfnt_swap32 (&head->created_low);
sfnt_swap32 (&head->modified_high);
sfnt_swap32 (&head->modified_low);
sfnt_swap16 (&head->xmin);
sfnt_swap16 (&head->xmax);
sfnt_swap16 (&head->ymin);
sfnt_swap16 (&head->ymax);
sfnt_swap16 (&head->mac_style);
sfnt_swap16 (&head->lowest_rec_ppem);
sfnt_swap16 (&head->font_direction_hint);
sfnt_swap16 (&head->index_to_loc_format);
sfnt_swap16 (&head->glyph_data_format);
return head;
}
/* Read the hhea table of a given font FD. Use the table directory
specified in SUBTABLE.
Return the head table upon success, else NULL. */
TEST_STATIC struct sfnt_hhea_table *
sfnt_read_hhea_table (int fd, struct sfnt_offset_subtable *subtable)
{
struct sfnt_table_directory *directory;
struct sfnt_hhea_table *hhea;
ssize_t rc;
/* Find the table in the directory. */
directory = sfnt_find_table (subtable, SFNT_TABLE_HHEA);
if (!directory)
return NULL;
/* Check the length is right. */
if (directory->length != sizeof *hhea)
return NULL;
/* Seek to the location given in the directory. */
if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
return NULL;
/* Read the entire table. */
hhea = xmalloc (sizeof *hhea);
rc = read (fd, hhea, sizeof *hhea);
if (rc < (int) sizeof *hhea)
{
xfree (hhea);
return NULL;
}
/* Swap the header data. */
sfnt_swap32 (&hhea->version);
if (hhea->version != 0x00010000)
{
xfree (hhea);
return NULL;
}
/* Swap the rest of the data. */
sfnt_swap16 (&hhea->ascent);
sfnt_swap16 (&hhea->descent);
sfnt_swap16 (&hhea->line_gap);
sfnt_swap16 (&hhea->advance_width_max);
sfnt_swap16 (&hhea->min_left_side_bearing);
sfnt_swap16 (&hhea->min_right_side_bearing);
sfnt_swap16 (&hhea->x_max_extent);
sfnt_swap16 (&hhea->caret_slope_rise);
sfnt_swap16 (&hhea->caret_slope_run);
sfnt_swap16 (&hhea->reserved1);
sfnt_swap16 (&hhea->reserved2);
sfnt_swap16 (&hhea->reserved3);
sfnt_swap16 (&hhea->reserved4);
sfnt_swap16 (&hhea->metric_data_format);
sfnt_swap16 (&hhea->num_of_long_hor_metrics);
return hhea;
}
/* Read a short loca table from the given font FD. Use the table
directory specified in SUBTABLE.
Return the short table upon success, else NULL. */
TEST_STATIC struct sfnt_loca_table_short *
sfnt_read_loca_table_short (int fd, struct sfnt_offset_subtable *subtable)
{
struct sfnt_table_directory *directory;
struct sfnt_loca_table_short *loca;
ssize_t rc;
int i;
/* Find the table in the directory. */
directory = sfnt_find_table (subtable, SFNT_TABLE_LOCA);
if (!directory)
return NULL;
/* Seek to the location given in the directory. */
if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
return NULL;
/* Figure out how many glyphs there are based on the length. */
loca = xmalloc (sizeof *loca + directory->length);
loca->offsets = (uint16_t *) (loca + 1);
loca->num_offsets = directory->length / 2;
/* Read the variable-length table data. */
rc = read (fd, loca->offsets, directory->length);
if (rc < directory->length)
{
xfree (loca);
return NULL;
}
/* Swap each of the offsets. */
for (i = 0; i < loca->num_offsets; ++i)
sfnt_swap16 (&loca->offsets[i]);
/* Return the table. */
return loca;
}
/* Read a long loca table from the given font FD. Use the table
directory specified in SUBTABLE.
Return the long table upon success, else NULL. */
TEST_STATIC struct sfnt_loca_table_long *
sfnt_read_loca_table_long (int fd, struct sfnt_offset_subtable *subtable)
{
struct sfnt_table_directory *directory;
struct sfnt_loca_table_long *loca;
ssize_t rc;
int i;
/* Find the table in the directory. */
directory = sfnt_find_table (subtable, SFNT_TABLE_LOCA);
if (!directory)
return NULL;
/* Seek to the location given in the directory. */
if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
return NULL;
/* Figure out how many glyphs there are based on the length. */
loca = xmalloc (sizeof *loca + directory->length);
loca->offsets = (uint32_t *) (loca + 1);
loca->num_offsets = directory->length / 4;
/* Read the variable-length table data. */
rc = read (fd, loca->offsets, directory->length);
if (rc < directory->length)
{
xfree (loca);
return NULL;
}
/* Swap each of the offsets. */
for (i = 0; i < loca->num_offsets; ++i)
sfnt_swap32 (&loca->offsets[i]);
/* Return the table. */
return loca;
}
/* Read the maxp table from the given font FD. Use the table
directory specified in SUBTABLE.
Return the maxp table upon success, else NULL. If the version is
0.5, fields past num_glyphs will not be populated. */
TEST_STATIC struct sfnt_maxp_table *
sfnt_read_maxp_table (int fd, struct sfnt_offset_subtable *subtable)
{
struct sfnt_table_directory *directory;
struct sfnt_maxp_table *maxp;
size_t size;
ssize_t rc;
/* Find the table in the directory. */
directory = sfnt_find_table (subtable, SFNT_TABLE_MAXP);
if (!directory)
return NULL;
/* Seek to the location given in the directory. */
if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
return NULL;
/* If directory->length is not big enough for version 0.5, punt. */
if (directory->length < SFNT_ENDOF (struct sfnt_maxp_table,
num_glyphs, uint16_t))
return NULL;
/* Allocate the buffer to hold the data. Then, read
directory->length or sizeof *maxp bytes into it, whichever is
smaller. */
maxp = xmalloc (sizeof *maxp);
size = MIN (directory->length, sizeof *maxp);
rc = read (fd, maxp, size);
if (rc == -1 || rc < size)
{
xfree (maxp);
return NULL;
}
/* Now, swap version and num_glyphs. */
sfnt_swap32 (&maxp->version);
sfnt_swap16 (&maxp->num_glyphs);
/* Reject version 1.0 tables that are too small. */
if (maxp->version > 0x00005000 && size < sizeof *maxp)
{
xfree (maxp);
return NULL;
}
/* If the table is version 0.5, then this function is done. */
if (maxp->version == 0x00005000)
return maxp;
else if (maxp->version != 0x00010000)
{
/* Reject invalid versions. */
xfree (maxp);
return NULL;
}
/* Otherwise, swap the rest of the fields. */
sfnt_swap16 (&maxp->max_points);
sfnt_swap16 (&maxp->max_contours);
sfnt_swap16 (&maxp->max_composite_points);
sfnt_swap16 (&maxp->max_composite_contours);
sfnt_swap16 (&maxp->max_zones);
sfnt_swap16 (&maxp->max_twilight_points);
sfnt_swap16 (&maxp->max_storage);
sfnt_swap16 (&maxp->max_function_defs);
sfnt_swap16 (&maxp->max_instruction_defs);
sfnt_swap16 (&maxp->max_stack_elements);
sfnt_swap16 (&maxp->max_size_of_instructions);
sfnt_swap16 (&maxp->max_component_elements);
sfnt_swap16 (&maxp->max_component_depth);
/* All done. */
return maxp;
}
/* Glyph outlining generation. */
/* Read a glyf table from the given font FD. Use the table directory
specified in SUBTABLE. The glyph data is not swapped.
Return the glyf table upon success, else NULL. */
TEST_STATIC struct sfnt_glyf_table *
sfnt_read_glyf_table (int fd, struct sfnt_offset_subtable *subtable)
{
struct sfnt_table_directory *directory;
struct sfnt_glyf_table *glyf;
ssize_t rc;
/* Find the table in the directory. */
directory = sfnt_find_table (subtable, SFNT_TABLE_GLYF);
if (!directory)
return NULL;
/* Seek to the location given in the directory. */
if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
return NULL;
/* Allocate enough to hold everything. */
glyf = xmalloc (sizeof *glyf + directory->length);
glyf->size = directory->length;
glyf->glyphs = (unsigned char *) (glyf + 1);
/* Read the glyph data. */
rc = read (fd, glyf->glyphs, glyf->size);
if (rc == -1 || rc < glyf->size)
{
xfree (glyf);
return NULL;
}
/* Return the table. */
return glyf;
}
#if defined HAVE_MMAP && !defined TEST
/* Map a glyph table from the given font FD. Use the table directory
specified in SUBTABLE. The glyph data is not byte-swapped.
Value is the glyf table upon success, else NULL.
A mapped glyf table must be unmapped using `sfnt_unmap_glyf_table'.
The caller must correctly handle bus errors in between glyf->table
and glyf->size. */
struct sfnt_glyf_table *
sfnt_map_glyf_table (int fd, struct sfnt_offset_subtable *subtable)
{
struct sfnt_table_directory *directory;
struct sfnt_glyf_table *glyf;
void *glyphs;
size_t offset, page, map_offset;
/* Find the table in the directory. */
directory = sfnt_find_table (subtable, SFNT_TABLE_GLYF);
if (!directory)
return NULL;
/* 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. */
glyphs = mmap (NULL, directory->length + map_offset,
PROT_READ, MAP_PRIVATE, fd, offset);
if (glyphs == MAP_FAILED)
return NULL;
/* An observation is that glyphs tend to be accessed in sequential
order and immediately after the font's glyph table is loaded. */
#ifdef HAVE_POSIX_MADVISE
posix_madvise (glyphs, directory->length,
POSIX_MADV_WILLNEED);
#elif defined HAVE_MADVISE
madvise (glyphs, directory->length, MADV_WILLNEED);
#endif
/* Allocate the glyf table. */
glyf = xmalloc (sizeof *glyf);
glyf->size = directory->length;
glyf->glyphs = (unsigned char *) glyphs + map_offset;
glyf->start = glyphs;
return glyf;
}
/* Unmap the mmap'ed glyf table GLYF, then free its associated data.
Value is 0 upon success, else 1, in which case GLYF is still freed
all the same. */
int
sfnt_unmap_glyf_table (struct sfnt_glyf_table *glyf)
{
int rc;
size_t size;
/* Calculate the size of the mapping. */
size = glyf->size + (glyf->glyphs - glyf->start);
rc = munmap (glyf->start, size);
xfree (glyf);
return rc != 0;
}
#endif /* HAVE_MMAP */
/* Read the simple glyph outline from the glyph GLYPH from the
specified glyf table at the given offset. Set GLYPH->simple to a
non-NULL value upon success, else set it to NULL. */
static void
sfnt_read_simple_glyph (struct sfnt_glyph *glyph,
struct sfnt_glyf_table *glyf,
size_t offset)
{
struct sfnt_simple_glyph *simple;
ssize_t min_size, min_size_2;
int i, number_of_points, repeat_count;
unsigned char *instructions_start;
unsigned char *flags_start, *flags_end;
unsigned char *vec_start;
int16_t delta, x, y;
/* Calculate the minimum size of the glyph data. This is the size
of the instruction length field followed by
glyph->number_of_contours * sizeof (uint16_t). */
min_size = (glyph->number_of_contours * sizeof (uint16_t)
+ sizeof (uint16_t));
/* Check that the size is big enough. */
if (glyf->size < offset + min_size)
{
glyph->simple = NULL;
return;
}
/* Allocate enough to read at least that. */
simple = xmalloc (sizeof *simple + min_size);
simple->end_pts_of_contours = (uint16_t *) (simple + 1);
memcpy (simple->end_pts_of_contours, glyf->glyphs + offset,
min_size);
/* This is not really an index into simple->end_pts_of_contours.
Rather, it is reading the first word past it. */
simple->instruction_length
= simple->end_pts_of_contours[glyph->number_of_contours];
/* Swap the contour end point indices and the instruction
length. */
for (i = 0; i < glyph->number_of_contours; ++i)
sfnt_swap16 (&simple->end_pts_of_contours[i]);
sfnt_swap16 (&simple->instruction_length);
/* Based on those values, calculate the maximum size of the
following data. This is the instruction length + the last
contour point + the last contour point * uint16_t * 2. */
if (glyph->number_of_contours)
number_of_points
= simple->end_pts_of_contours[glyph->number_of_contours - 1] + 1;
else
number_of_points = 0;
min_size_2 = (simple->instruction_length
+ number_of_points
+ (number_of_points
* sizeof (uint16_t) * 2));
/* Set simple->number_of_points. */
simple->number_of_points = number_of_points;
/* Make simple big enough. */
simple = xrealloc (simple, sizeof *simple + min_size + min_size_2);
simple->end_pts_of_contours = (uint16_t *) (simple + 1);
/* Set the instruction data pointer and other pointers.
simple->instructions comes one word past number_of_contours,
because end_pts_of_contours also contains the instruction
length. */
simple->x_coordinates = (int16_t *) (simple->end_pts_of_contours
+ glyph->number_of_contours + 1);
simple->y_coordinates = simple->x_coordinates + number_of_points;
simple->instructions = (uint8_t *) (simple->y_coordinates + number_of_points);
simple->flags = simple->instructions + simple->instruction_length;
/* Read instructions into the glyph. */
instructions_start = glyf->glyphs + offset + min_size;
if (instructions_start >= glyf->glyphs + glyf->size
|| (instructions_start + simple->instruction_length
>= glyf->glyphs + glyf->size))
{
glyph->simple = NULL;
xfree (simple);
return;
}
memcpy (simple->instructions, instructions_start,
simple->instruction_length);
/* Start reading flags. */
flags_start = (glyf->glyphs + offset
+ min_size + simple->instruction_length);
flags_end = flags_start + number_of_points;
if (flags_start >= glyf->glyphs + glyf->size)
{
glyph->simple = NULL;
xfree (simple);
return;
}
i = 0;
while (flags_start < flags_end)
{
if (i == number_of_points)
break;
if (flags_start >= glyf->glyphs + glyf->size)
break;
simple->flags[i++] = *flags_start;
if (*flags_start & 010) /* REPEAT_FLAG */
{
/* The next byte specifies how many times this byte is to be
repeated. Check that it is in range. */
if (flags_start + 1 >= glyf->glyphs + glyf->size)
{
glyph->simple = NULL;
xfree (simple);
return;
}
/* Repeat the current flag until
glyph->number_of_points. */
repeat_count = *(flags_start + 1);
while (i < number_of_points && repeat_count)
{
simple->flags[i++] = *flags_start;
repeat_count--;
}
/* Skip one byte in flags_start. */
flags_start++;
}
flags_start++;
}
/* If an insufficient number of flags have been read, then the
outline is invalid. */
if (i != number_of_points)
{
glyph->simple = NULL;
xfree (simple);
return;
}
/* Now that the flags have been decoded, start decoding the
vectors. */
vec_start = flags_start;
i = 0;
x = 0;
/* flags_start is now repurposed to act as a pointer to the flags
for the current vector! */
flags_start = simple->flags;
while (i < number_of_points)
{
delta = 0;
if ((*flags_start) & 02) /* X_SHORT_VECTOR */
{
/* The next byte is a delta to apply to the previous
value. Make sure it is in bounds. */
if (vec_start + 1 > glyf->glyphs + glyf->size)
{
glyph->simple = NULL;
xfree (simple);
return;
}
delta = *vec_start++;
if (!(*flags_start & 020)) /* SAME_X */
delta = -delta;
}
else if (!(*flags_start & 020)) /* SAME_X */
{
/* The next word is a delta to apply to the previous value.
Make sure it is in bounds. */
if (vec_start + 2 > glyf->glyphs + glyf->size)
{
glyph->simple = NULL;
xfree (simple);
return;
}
/* Read the unaligned word and swap it. */
memcpy (&delta, vec_start, sizeof delta);
sfnt_swap16 (&delta);
vec_start += 2;
}
/* Apply the delta and set the X value. */
x += delta;
simple->x_coordinates[i++] = x;
flags_start++;
}
/* Decode the Y vector. flags_start is again repurposed to act as a
pointer to the flags for the current vector. */
flags_start = simple->flags;
y = 0;
i = 0;
while (i < number_of_points)
{
delta = 0;
if (*flags_start & 04) /* Y_SHORT_VECTOR */
{
/* The next byte is a delta to apply to the previous
value. Make sure it is in bounds. */
if (vec_start + 1 > glyf->glyphs + glyf->size)
{
glyph->simple = NULL;
xfree (simple);
return;
}
delta = *vec_start++;
if (!(*flags_start & 040)) /* SAME_Y */
delta = -delta;
}
else if (!(*flags_start & 040)) /* SAME_Y */
{
/* The next word is a delta to apply to the previous value.
Make sure it is in bounds. */
if (vec_start + 2 > glyf->glyphs + glyf->size)
{
glyph->simple = NULL;
xfree (simple);
return;
}
/* Read the unaligned word and swap it. */
memcpy (&delta, vec_start, sizeof delta);
sfnt_swap16 (&delta);
vec_start += 2;
}
/* Apply the delta and set the X value. */
y += delta;
simple->y_coordinates[i++] = y;
flags_start++;
}
/* All done. */
simple->y_coordinates_end = simple->y_coordinates + i;
glyph->simple = simple;
return;
}
/* Read the compound glyph outline from the glyph GLYPH from the
specified glyf table at the given offset. Set GLYPH->compound to a
non-NULL value upon success, else set it to NULL. */
static void
sfnt_read_compound_glyph (struct sfnt_glyph *glyph,
struct sfnt_glyf_table *glyf,
size_t offset)
{
uint16_t flags, instruction_length, words[2], words4[4];
size_t required_bytes, num_components, i;
unsigned char *data, *instruction_base;
/* Assume failure for now. Figure out how many bytes have to be
allocated by reading the compound data. */
glyph->compound = NULL;
required_bytes = 0;
num_components = 0;
data = glyf->glyphs + offset;
/* Offset could be unaligned. */
do
{
if (data + 2 > glyf->glyphs + glyf->size)
return;
memcpy (&flags, data, sizeof flags);
sfnt_swap16 (&flags);
data += sizeof flags;
/* Require at least one structure to hold this data. */
required_bytes += sizeof (struct sfnt_compound_glyph_component);
num_components++;
/* Skip past unused data. */
data += 2;
if (flags & 01) /* ARG_1_AND_2_ARE_WORDS */
data += sizeof (int16_t) * 2;
else
data += sizeof (int8_t) * 2;
if (flags & 010) /* WE_HAVE_A_SCALE */
data += sizeof (uint16_t);
else if (flags & 0100) /* WE_HAVE_AN_X_AND_Y_SCALE */
data += sizeof (uint16_t) * 2;
else if (flags & 0200) /* WE_HAVE_A_TWO_BY_TWO */
data += sizeof (uint16_t) * 4;
}
while (flags & 040); /* MORE_COMPONENTS */
if (flags & 0400) /* WE_HAVE_INSTRUCTIONS */
{
/* Figure out the instruction length. */
if (data + 2 > glyf->glyphs + glyf->size)
return;
/* Now see how much is required to hold the instruction
data. */
memcpy (&instruction_length, data,
sizeof instruction_length);
sfnt_swap16 (&instruction_length);
required_bytes += instruction_length;
data += sizeof data + instruction_length;
}
/* Now allocate the buffer to hold all the glyph data. */
glyph->compound = xmalloc (sizeof *glyph->compound
+ required_bytes);
glyph->compound->components
= (struct sfnt_compound_glyph_component *) (glyph->compound + 1);
glyph->compound->num_components = num_components;
/* Figure out where instruction data starts. It comes after
glyph->compound->components ends. */
instruction_base
= (unsigned char *) (glyph->compound->components
+ glyph->compound->num_components);
/* Start reading. */
i = 0;
data = glyf->glyphs + offset;
do
{
if (data + 4 > glyf->glyphs + glyf->size)
{
xfree (glyph->compound);
glyph->compound = NULL;
return;
}
memcpy (&flags, data, sizeof flags);
sfnt_swap16 (&flags);
data += sizeof flags;
glyph->compound->components[i].flags = flags;
memcpy (&glyph->compound->components[i].glyph_index,
data, sizeof glyph->compound->components[i].glyph_index);
sfnt_swap16 (&glyph->compound->components[i].glyph_index);
data += sizeof glyph->compound->components[i].glyph_index;
if (flags & 01) /* ARG_1_AND_2_ARE_WORDS. */
{
if (data + 4 > glyf->glyphs + glyf->size)
{
xfree (glyph->compound);
glyph->compound = NULL;
return;
}
/* Read two words into arg1 and arg2. */
memcpy (words, data, sizeof words);
sfnt_swap16 (&words[0]);
sfnt_swap16 (&words[1]);
glyph->compound->components[i].argument1.c = words[0];
glyph->compound->components[i].argument2.c = words[1];
data += sizeof words;
}
else
{
if (data + 2 > glyf->glyphs + glyf->size)
{
xfree (glyph->compound);
glyph->compound = NULL;
return;
}
/* Read two bytes into arg1 and arg2. */
glyph->compound->components[i].argument1.a = data[0];
glyph->compound->components[i].argument2.a = data[1];
data += 2;
}
if (flags & 010) /* WE_HAVE_A_SCALE */
{
if (data + 2 > glyf->glyphs + glyf->size)
{
xfree (glyph->compound);
glyph->compound = NULL;
return;
}
/* Read one word into scale. */
memcpy (&glyph->compound->components[i].u.scale, data,
sizeof glyph->compound->components[i].u.scale);
sfnt_swap16 (&glyph->compound->components[i].u.scale);
data += sizeof glyph->compound->components[i].u.scale;
}
else if (flags & 0100) /* WE_HAVE_AN_X_AND_Y_SCALE. */
{
if (data + 4 > glyf->glyphs + glyf->size)
{
xfree (glyph->compound);
glyph->compound = NULL;
return;
}
/* Read two words into xscale and yscale. */
memcpy (words, data, sizeof words);
sfnt_swap16 (&words[0]);
sfnt_swap16 (&words[1]);
glyph->compound->components[i].u.a.xscale = words[0];
glyph->compound->components[i].u.a.yscale = words[1];
data += sizeof words;
}
else if (flags & 0200) /* WE_HAVE_A_TWO_BY_TWO */
{
if (data + 8 > glyf->glyphs + glyf->size)
{
xfree (glyph->compound);
glyph->compound = NULL;
return;
}
/* Read 4 words into the transformation matrix. */
memcpy (words4, data, sizeof words4);
sfnt_swap16 (&words4[0]);
sfnt_swap16 (&words4[1]);
sfnt_swap16 (&words4[2]);
sfnt_swap16 (&words4[3]);
glyph->compound->components[i].u.b.xscale = words4[0];
glyph->compound->components[i].u.b.scale01 = words4[1];
glyph->compound->components[i].u.b.scale10 = words4[2];
glyph->compound->components[i].u.b.yscale = words4[3];
data += sizeof words4;
}
/* Record the component flags. */
glyph->compound->components[i].flags = flags;
i++;
}
while (flags & 040); /* MORE_COMPONENTS */
if (flags & 0400) /* WE_HAVE_INSTR */
{
/* Figure out the instruction length. */
if (data + 2 > glyf->glyphs + glyf->size)
{
xfree (glyph->compound);
glyph->compound = NULL;
return;
}
/* Now see how much is required to hold the instruction
data. */
memcpy (&glyph->compound->instruction_length,
data,
sizeof glyph->compound->instruction_length);
sfnt_swap16 (&glyph->compound->instruction_length);
data += 2;
/* Read the instructions. */
glyph->compound->instructions = instruction_base;
if (data + glyph->compound->instruction_length
> glyf->glyphs + glyf->size)
{
xfree (glyph->compound);
glyph->compound = NULL;
return;
}
memcpy (instruction_base, data,
glyph->compound->instruction_length);
}
else
{
glyph->compound->instructions = NULL;
glyph->compound->instruction_length = 0;
}
/* Data read successfully. */
return;
}
/* Read the description of the glyph GLYPH_CODE from the specified
glyf table, using the offsets of LOCA_SHORT or LOCA_LONG, depending
on which is non-NULL. */
TEST_STATIC struct sfnt_glyph *
sfnt_read_glyph (sfnt_glyph glyph_code,
struct sfnt_glyf_table *glyf,
struct sfnt_loca_table_short *loca_short,
struct sfnt_loca_table_long *loca_long)
{
struct sfnt_glyph glyph, *memory;
size_t offset, next_offset;
/* Check the glyph code is within bounds. */
if (glyph_code > 65535)
return NULL;
if (loca_short)
{
/* Check that the glyph is within bounds. glyph_code + 1 is the
entry in the table which defines the length of the glyph. */
if (glyph_code + 1 >= loca_short->num_offsets)
return NULL;
offset = loca_short->offsets[glyph_code] * 2;
next_offset = loca_short->offsets[glyph_code + 1] * 2;
}
else if (loca_long)
{
if (glyph_code + 1 >= loca_long->num_offsets)
return NULL;
offset = loca_long->offsets[glyph_code];
next_offset = loca_long->offsets[glyph_code + 1];
}
else
abort ();
/* If offset - next_offset is 0, then the glyph is empty. Its
horizontal advance may still be provided by the hmtx table. */
if (offset == next_offset)
{
glyph.number_of_contours = 0;
glyph.xmin = 0;
glyph.ymin = 0;
glyph.xmax = 0;
glyph.ymax = 0;
glyph.advance_distortion = 0;
glyph.origin_distortion = 0;
glyph.simple = xmalloc (sizeof *glyph.simple);
glyph.compound = NULL;
memset (glyph.simple, 0, sizeof *glyph.simple);
memory = xmalloc (sizeof *memory);
*memory = glyph;
return memory;
}
/* Verify that GLYF is big enough to hold a glyph at OFFSET. */
if (glyf->size < offset + SFNT_ENDOF (struct sfnt_glyph,
ymax, sfnt_fword))
return NULL;
/* Copy over the glyph data. */
memcpy (&glyph, glyf->glyphs + offset,
SFNT_ENDOF (struct sfnt_glyph,
ymax, sfnt_fword));
/* Swap the glyph data. */
sfnt_swap16 (&glyph.number_of_contours);
sfnt_swap16 (&glyph.xmin);
sfnt_swap16 (&glyph.ymin);
sfnt_swap16 (&glyph.xmax);
sfnt_swap16 (&glyph.ymax);
/* This is set later on after `sfnt_vary_X_glyph'. */
glyph.advance_distortion = 0;
glyph.origin_distortion = 0;
/* Figure out what needs to be read based on
glyph.number_of_contours. */
if (glyph.number_of_contours >= 0)
{
/* Read the simple glyph. */
glyph.compound = NULL;
sfnt_read_simple_glyph (&glyph, glyf,
offset + SFNT_ENDOF (struct sfnt_glyph,
ymax, sfnt_fword));
if (glyph.simple)
{
memory = xmalloc (sizeof glyph);
*memory = glyph;
return memory;
}
}
else
{
/* Read the compound glyph. */
glyph.simple = NULL;
sfnt_read_compound_glyph (&glyph, glyf,
offset + SFNT_ENDOF (struct sfnt_glyph,
ymax, sfnt_fword));
if (glyph.compound)
{
memory = xmalloc (sizeof glyph);
*memory = glyph;
return memory;
}
}
return NULL;
}
/* Free a glyph returned from sfnt_read_glyph. GLYPH may be NULL. */
TEST_STATIC void
sfnt_free_glyph (struct sfnt_glyph *glyph)
{
if (!glyph)
return;
xfree (glyph->simple);
xfree (glyph->compound);
xfree (glyph);
}
/* Glyph outline decomposition. */
/* Apply the transform in the compound glyph component COMPONENT to
the array of points of length NUM_COORDINATES given as X and Y.
Also, apply the fixed point offsets X_OFF and Y_OFF to each X and Y
coordinate after transforms within COMPONENT are effected. */
static void
sfnt_transform_coordinates (struct sfnt_compound_glyph_component *component,
sfnt_fixed *restrict x, sfnt_fixed *restrict y,
size_t num_coordinates,
sfnt_fixed x_off, sfnt_fixed y_off)
{
double m1, m2, m3;
double m4, m5, m6;
size_t i;
if (component->flags & 010) /* WE_HAVE_A_SCALE */
{
m1 = component->u.scale / 16384.0;
m2 = m3 = m4 = 0;
m5 = component->u.scale / 16384.0;
m6 = 0;
}
else if (component->flags & 0100) /* WE_HAVE_AN_X_AND_Y_SCALE */
{
m1 = component->u.a.xscale / 16384.0;
m2 = m3 = m4 = 0;
m5 = component->u.a.yscale / 16384.0;
m6 = 0;
}
else if (component->flags & 0200) /* WE_HAVE_A_TWO_BY_TWO */
{
m1 = component->u.b.xscale / 16384.0;
m2 = component->u.b.scale01 / 16384.0;
m3 = 0;
m4 = component->u.b.scale10 / 16384.0;
m5 = component->u.b.yscale / 16384.0;
m6 = 0;
}
else /* No scale, just apply x_off and y_off. */
{
for (i = 0; i < num_coordinates; ++i)
x[i] += x_off, y[i] += y_off;
return;
}
m3 = x_off;
m6 = y_off;
/* Apply the specified affine transformation.
A transform looks like:
M1 M2 M3 X
M4 M5 M6 * Y
=
M1*X + M2*Y + M3*1 = X1
M4*X + M5*Y + M6*1 = Y1
(In most transforms, there is another row at the bottom for
mathematical reasons. Since Z1 is always 1.0, the row is simply
implied to be 0 0 1, because 0 * x + 0 * y + 1 * 1 = 1.0. See
the definition of matrix3x3 in image.c for some more explanations
about this.) */
for (i = 0; i < num_coordinates; ++i)
{
x[i] = m1 * x[i] + m2 * y[i] + m3 * 1;
y[i] = m4 * x[i] + m5 * y[i] + m6 * 1;
}
}
struct sfnt_compound_glyph_context
{
/* Arrays of points. The underlying type is actually sfnt_f26dot6
when instructing a compound glyph. */
sfnt_fixed *x_coordinates, *y_coordinates;
/* Array of flags for the points. */
unsigned char *flags;
/* Number of points in that array, and the size of that array. */
size_t num_points, points_size;
/* Array of contour end points. */
size_t *contour_end_points;
/* Number of elements in and the size of that array. */
size_t num_end_points, end_points_size;
/* The X positions of two phantom points marking this glyph's origin
and advance position, only used while interpreting the glyph. */
sfnt_f26dot6 phantom_point_1_x, phantom_point_2_x;
/* Y positions. */
sfnt_f26dot6 phantom_point_1_y, phantom_point_2_y;
/* Unrounded X positions. */
sfnt_f26dot6 phantom_point_1_s, phantom_point_2_s;
};
/* Extend the arrays inside the compound glyph decomposition context
CONTEXT. NUMBER_OF_CONTOURS is the number of contours to add.
NUMBER_OF_POINTS is the number of points to add.
Return pointers to the beginning of the extension in *X_BASE,
*Y_BASE, *FLAGS_BASE and *CONTOUR_BASE. Value zero upon success,
and something else on failure. */
static int
sfnt_expand_compound_glyph_context (struct sfnt_compound_glyph_context *context,
size_t number_of_contours,
size_t number_of_points,
sfnt_fixed **x_base, sfnt_fixed **y_base,
unsigned char **flags_base,
size_t **contour_base)
{
size_t size_bytes;
/* Add each field while checking for overflow. */
if (ckd_add (&context->num_end_points, number_of_contours,
context->num_end_points))
return 1;
if (ckd_add (&context->num_points, number_of_points, context->num_points))
return 1;
/* Reallocate each array to the new size if necessary. */
if (context->points_size < context->num_points)
{
if (ckd_mul (&context->points_size, context->num_points, 2))
context->points_size = context->num_points;
if (ckd_mul (&size_bytes, context->points_size,
sizeof *context->x_coordinates))
return 1;
context->x_coordinates = xrealloc (context->x_coordinates,
size_bytes);
context->y_coordinates = xrealloc (context->y_coordinates,
size_bytes);
context->flags = xrealloc (context->flags,
context->points_size);
}
/* Set x_base and y_base. */
*x_base = (context->x_coordinates
+ context->num_points
- number_of_points);
*y_base = (context->y_coordinates
+ context->num_points
- number_of_points);
*flags_base = (context->flags
+ context->num_points
- number_of_points);
if (context->end_points_size < context->num_end_points)
{
if (ckd_mul (&context->end_points_size, context->num_end_points, 2))
context->end_points_size = context->num_end_points;
if (ckd_mul (&size_bytes, context->end_points_size,
sizeof *context->contour_end_points))
return 1;
context->contour_end_points
= xrealloc (context->contour_end_points,
size_bytes);
}
/* Set contour_base. */
*contour_base = (context->contour_end_points
+ context->num_end_points
- number_of_contours);
return 0;
}
/* Round the 16.16 fixed point number NUMBER to the nearest integral
value. */
static int32_t
sfnt_round_fixed (int32_t number)
{
/* Add 0.5... */
number += (1 << 15);
/* Remove the fractional. */
return number & ~0xffff;
}
/* Decompose GLYPH, a compound glyph, into an array of points and
contours.
CONTEXT should be zeroed and put on the stack. RECURSION_COUNT
should be initialized to 0. GET_GLYPH, FREE_GLYPH, and
GET_METRICS, along with DCONTEXT, mean the same as in
sfnt_decompose_glyph.
If it has been arranged that a component's metrics (or those of an
innermore component also with the flag set) replace the metrics of
GLYPH, set *METRICS_RETURN to those metrics. Mind that such
metrics are not scaled in any manner.
Value is 1 upon failure, else 0. */
static int
sfnt_decompose_compound_glyph (struct sfnt_glyph *glyph,
struct sfnt_compound_glyph_context *context,
struct sfnt_glyph_metrics *metrics_return,
sfnt_get_glyph_proc get_glyph,
sfnt_free_glyph_proc free_glyph,
sfnt_get_metrics_proc get_metrics,
int recursion_count,
void *dcontext)
{
struct sfnt_glyph *subglyph;
int i, j, rc;
bool need_free;
struct sfnt_compound_glyph_component *component;
sfnt_fixed x, y, xtemp, ytemp;
size_t point UNINIT, point2 UNINIT, index;
uint16_t last_point, number_of_contours;
sfnt_fixed *x_base, *y_base;
size_t *contour_base;
unsigned char *flags_base;
size_t base_index, contour_start;
bool defer_offsets;
struct sfnt_glyph_metrics sub_metrics;
sfnt_fixed f1, f2;
/* Set up the base index. This is the index from where on point
renumbering starts.
In other words, point 0 in this glyph will be 0 + base_index,
point 1 will be 1 + base_index, and so on. */
base_index = context->num_points;
/* Prevent infinite loops. Simply limit the level of nesting to the
maximum valid value of `max_component_depth', which is 16. */
if (recursion_count > 16)
return 1;
for (j = 0; j < glyph->compound->num_components; ++j)
{
/* Look up the associated subglyph. */
component = &glyph->compound->components[j];
subglyph = get_glyph (component->glyph_index,
dcontext, &need_free);
if (!subglyph)
return 1;
/* Don't defer offsets. This variable is set if the component
glyph is a compound glyph that is anchored to a previously
decomposed point, and needs its coordinates adjusted after
decomposition completes. */
defer_offsets = false;
/* Record the size of the point array before expansion. This
will be the base to apply to all points coming from this
subglyph. */
contour_start = context->num_points;
/* Compute the offset for the component. */
if (component->flags & 02) /* ARGS_ARE_XY_VALUES */
{
/* Component offsets are X/Y values as opposed to points
GLYPH. */
if (!(component->flags & 01)) /* ARG_1_AND_2_ARE_WORDS */
{
/* X and Y are signed bytes. */
x = component->argument1.b * 65536;
y = component->argument2.b * 65536;
}
else
{
/* X and Y are signed words. */
x = component->argument1.d * 65536;
y = component->argument2.d * 65536;
}
/* If there is some kind of scale and component offsets are
scaled, then apply the transform to the offset. */
if (component->flags & 04000) /* SCALED_COMPONENT_OFFSET */
sfnt_transform_coordinates (component, &x, &y, 1,
0, 0);
}
else
{
/* The offset is determined by matching a point location in
a preceding component with a point location in the
current component. The index of the point in the
previous component can be determined by adding
component->argument1.a or component->argument1.c to
point. argument2 contains the index of the point in the
current component. */
if (!(component->flags & 01)) /* ARG_1_AND_2_ARE_WORDS */
{
point = base_index + component->argument1.a;
point2 = component->argument2.a;
}
else
{
point = base_index + component->argument1.c;
point2 = component->argument2.c;
}
/* Now, check that the anchor point specified lies inside
the glyph. */
if (point >= contour_start)
{
if (need_free)
free_glyph (subglyph, dcontext);
return 1;
}
if (!subglyph->compound)
{
if (point2 >= subglyph->simple->number_of_points)
{
if (point2 < subglyph->simple->number_of_points + 2)
{
/* POINT2 is one of SUBGLYPH's phantom points.
Retrieve the glyph's metrics. */
if ((*get_metrics) (component->glyph_index, &sub_metrics,
dcontext))
{
if (need_free)
free_glyph (subglyph, dcontext);
return 1;
}
/* Derive the phantom points from those metrics. */
f1 = glyph->xmin - sub_metrics.lbearing;
f2 = f1 + sub_metrics.advance;
/* Apply the metrics distortion. */
f1 += glyph->origin_distortion;
f2 += glyph->advance_distortion;
/* Get the points and use them to compute the offsets. */
if (!(point2 - subglyph->simple->number_of_points))
x = f1 * 65536;
else
x = f2 * 65536;
x = context->x_coordinates[point] - x;
y = context->y_coordinates[point];
/* X and Y offsets have been ascertained. */
goto skip_computation;
}
if (need_free)
free_glyph (subglyph, dcontext);
return 1;
}
/* Get the points and use them to compute the offsets. */
xtemp = context->x_coordinates[point];
ytemp = context->y_coordinates[point];
x = (xtemp - subglyph->simple->x_coordinates[point2] * 65536);
y = (ytemp - subglyph->simple->y_coordinates[point2] * 65536);
skip_computation:
;
}
else
{
/* First, set offsets to 0, because it is not yet
possible to determine the position of the anchor
point in the child. */
x = 0;
y = 0;
/* Set a flag which indicates that offsets must be
resolved from the child glyph after it is loaded, but
before it is incorporated into the parent glyph. */
defer_offsets = true;
}
}
if (subglyph->simple)
{
/* Simple subglyph. Copy over the points and contours, and
transform them. */
if (subglyph->number_of_contours)
{
index = subglyph->number_of_contours - 1;
last_point
= subglyph->simple->end_pts_of_contours[index];
number_of_contours = subglyph->number_of_contours;
/* Grow various arrays. */
rc = sfnt_expand_compound_glyph_context (context,
/* Number of
new contours
required. */
number_of_contours,
/* Number of new
points
required. */
last_point + 1,
&x_base,
&y_base,
&flags_base,
&contour_base);
if (rc)
{
if (need_free)
free_glyph (subglyph, dcontext);
return 1;
}
for (i = 0; i <= last_point; ++i)
{
x_base[i] = (subglyph->simple->x_coordinates[i] * 65536);
y_base[i] = (subglyph->simple->y_coordinates[i] * 65536);
flags_base[i] = subglyph->simple->flags[i];
}
/* Apply the transform to the points. */
sfnt_transform_coordinates (component, x_base, y_base,
last_point + 1, x, y);
/* Copy over the contours. */
for (i = 0; i < number_of_contours; ++i)
contour_base[i]
= (contour_start
+ subglyph->simple->end_pts_of_contours[i]);
/* If USE_MY_METRICS is present within this component,
save its metrics within *METRICS_RETURN. */
if (component->flags & 01000 /* USE_MY_METRICS */)
{
if ((*get_metrics) (component->glyph_index,
metrics_return, dcontext))
{
if (need_free)
free_glyph (subglyph, dcontext);
return 1;
}
/* Refer to the comment above sfnt_decompose_glyph
for reasons and manner in which these offsets are
applied. */
metrics_return->lbearing -= subglyph->origin_distortion;
metrics_return->advance += subglyph->advance_distortion;
}
}
}
else
{
/* If USE_MY_METRICS, save this subglyph's metrics within
sub_metrics; they might be overwritten by metrics for
subglyphs of this compound subglyph in turn. */
if (component->flags & 01000 /* USE_MY_METRICS */)
{
if ((*get_metrics) (component->glyph_index,
&sub_metrics, dcontext))
{
if (need_free)
free_glyph (subglyph, dcontext);
return 1;
}
/* Refer to the comment above sfnt_decompose_glyph for
reasons and manner in which these offsets are
applied. */
sub_metrics.lbearing -= subglyph->origin_distortion;
sub_metrics.advance += subglyph->advance_distortion;
}
/* Compound subglyph. Decompose the glyph recursively, and
then apply the transform. */
rc = sfnt_decompose_compound_glyph (subglyph,
context,
&sub_metrics,
get_glyph,
free_glyph,
get_metrics,
recursion_count + 1,
dcontext);
if (rc)
{
if (need_free)
free_glyph (subglyph, dcontext);
return 1;
}
if (component->flags & 01000 /* USE_MY_METRICS */)
/* Save sub_metrics inside *metrics_return as stated
above. */
*metrics_return = sub_metrics;
/* When an anchor point is being used to translate the
glyph, and the subglyph in question is actually a
compound glyph, it is impossible to know which offset to
use until the compound subglyph has actually been loaded.
defer_offsets is set to true if these conditions apply,
whereupon the offset is calculated here, using the points
in the loaded child compound glyph. */
if (defer_offsets)
{
/* Renumber the non renumbered point2 to point into the
decomposed component. */
point2 += contour_start;
/* Next, check that the non-renumbered point being
anchored lies inside the glyph data that was
decomposed. */
if (point2 >= context->num_points)
{
/* POINT2 might fall within the phantom points of
that glyph. */
if (point2 - context->num_points < 2)
{
if ((*get_metrics) (component->glyph_index, &sub_metrics,
dcontext))
goto error_in_defer_offsets;
/* Derive the phantom points from those metrics. */
f1 = glyph->xmin - sub_metrics.lbearing;
f2 = f1 + sub_metrics.advance;
/* Apply the metrics distortion. */
f1 += glyph->origin_distortion;
f2 += glyph->advance_distortion;
/* Get the points and use them to compute the offsets. */
if (!(point2 - context->num_points))
x = f1 * 65536;
else
x = f2 * 65536;
x = context->x_coordinates[point] - x;
y = context->y_coordinates[point];
/* X and Y offsets have been ascertained. */
goto skip_computation_from_defer_offsets;
}
error_in_defer_offsets:
if (need_free)
free_glyph (subglyph, dcontext);
return 1;
}
/* Get the points and use them to compute the
offsets. */
xtemp = context->x_coordinates[point];
ytemp = context->y_coordinates[point];
x = (xtemp - context->x_coordinates[point2]);
y = (ytemp - context->y_coordinates[point2]);
skip_computation_from_defer_offsets:
;
}
sfnt_transform_coordinates (component,
context->x_coordinates + contour_start,
context->y_coordinates + contour_start,
context->num_points - contour_start,
x, y);
}
if (need_free)
free_glyph (subglyph, dcontext);
}
/* Decomposition is complete. CONTEXT now contains the adjusted
outlines of the entire compound glyph. */
return 0;
}
/* Linear-interpolate to a point halfway between the points specified
by CONTROL1 and CONTROL2. Put the result in RESULT. */
static void
sfnt_lerp_half (struct sfnt_point *control1, struct sfnt_point *control2,
struct sfnt_point *result)
{
result->x = control1->x + ((control2->x - control1->x) / 2);
result->y = control1->y + ((control2->y - control1->y) / 2);
}
/* Decompose contour data inside X, Y and FLAGS, between the indices
HERE and LAST. Call LINE_TO, CURVE_TO and MOVE_TO as appropriate,
with DCONTEXT as an argument. Apply SCALE to each point; SCALE
should be the factor necessary to turn points into 16.16 fixed
point.
Value is 1 upon failure, else 0. */
static int
sfnt_decompose_glyph_1 (size_t here, size_t last,
sfnt_move_to_proc move_to,
sfnt_line_to_proc line_to,
sfnt_curve_to_proc curve_to,
void *dcontext,
sfnt_fword *x,
sfnt_fword *y, unsigned char *flags,
int scale)
{
struct sfnt_point control1, control2, start, mid;
size_t i;
/* The contour is empty. */
if (here == last)
/* An empty contour, if redundant, is not necessarily invalid. */
return 0;
/* Move the pen to the start of the contour. Apparently some fonts
have off the curve points as the start of a contour, so when that
happens lerp between the first and last points. */
if (flags[here] & 01) /* On Curve */
{
control1.x = x[here] * scale;
control1.y = y[here] * scale;
start = control1;
}
else if (flags[last] & 01)
{
/* Start at the last point if it is on the curve. Here, the
start really becomes the middle of a spline. */
control1.x = x[last] * scale;
control1.y = y[last] * scale;
start = control1;
/* Curve back one point early. */
last -= 1;
here -= 1;
}
else
{
/* Lerp between the start and the end. */
control1.x = x[here] * scale;
control1.y = y[here] * scale;
control2.x = x[last] * scale;
control2.y = y[last] * scale;
sfnt_lerp_half (&control1, &control2, &start);
/* In either of these cases, start iterating from just here as
opposed to here + 1, since logically the contour now starts
from the last curve. */
here -= 1;
}
/* Move to the start. */
move_to (start, dcontext);
/* Now handle each point between here + 1 and last. */
i = here;
while (++i <= last)
{
/* If the point is on the curve, then draw a line here from the
last control point. */
if (flags[i] & 01)
{
control1.x = x[i] * scale;
control1.y = y[i] * scale;
line_to (control1, dcontext);
/* Move to the next point. */
continue;
}
/* Off the curve points are more interesting. They are handled
one by one, with points in between being interpolated, until
either the last point is reached or an on-curve point is
processed. First, load the initial control points. */
control1.x = x[i] * scale;
control1.y = y[i] * scale;
while (++i <= last)
{
/* Load this point. */
control2.x = x[i] * scale;
control2.y = y[i] * scale;
/* If this point is on the curve, curve directly to this
point. */
if (flags[i] & 01)
{
curve_to (control1, control2, dcontext);
goto continue_loop;
}
/* Calculate the point between here and the previous
point. */
sfnt_lerp_half (&control1, &control2, &mid);
/* Curve over there. */
curve_to (control1, mid, dcontext);
/* Reload the control point. */
control1 = control2;
}
/* Close the contour by curving back to start. */
curve_to (control1, start, dcontext);
/* Don't close the contour twice. */
goto exit;
continue_loop:
continue;
}
/* Close the contour with a line back to start. */
line_to (start, dcontext);
exit:
return 0;
}
/* Decompose contour data inside X, Y and FLAGS, between the indices
HERE and LAST. Call LINE_TO, CURVE_TO and MOVE_TO as appropriate,
with DCONTEXT as an argument. Apply SCALE to each point; SCALE
should be the factor necessary to turn points into 16.16 fixed
point.
This is the version of sfnt_decompose_glyph_1 which takes
sfnt_fixed (or sfnt_f26dot6) as opposed to sfnt_fword.
Value is 1 upon failure, else 0. */
static int
sfnt_decompose_glyph_2 (size_t here, size_t last,
sfnt_move_to_proc move_to,
sfnt_line_to_proc line_to,
sfnt_curve_to_proc curve_to,
void *dcontext,
sfnt_fixed *x,
sfnt_fixed *y, unsigned char *flags,
int scale)
{
struct sfnt_point control1, control2, start, mid;
size_t i;
/* The contour is empty. */
if (here == last)
/* An empty contour, if redundant, is not necessarily invalid. */
return 0;
/* Move the pen to the start of the contour. Apparently some fonts
have off the curve points as the start of a contour, so when that
happens lerp between the first and last points. */
if (flags[here] & 01) /* On Curve */
{
control1.x = x[here] * scale;
control1.y = y[here] * scale;
start = control1;
}
else if (flags[last] & 01)
{
/* Start at the last point if it is on the curve. Here, the
start really becomes the middle of a spline. */
control1.x = x[last] * scale;
control1.y = y[last] * scale;
start = control1;
/* Curve back one point early. */
last -= 1;
here -= 1;
}
else
{
/* Lerp between the start and the end. */
control1.x = x[here] * scale;
control1.y = y[here] * scale;
control2.x = x[last] * scale;
control2.y = y[last] * scale;
sfnt_lerp_half (&control1, &control2, &start);
/* In either of these cases, start iterating from just here as
opposed to here + 1, since logically the contour now starts
from the last curve. */
here -= 1;
}
/* Move to the start. */
move_to (start, dcontext);
/* Now handle each point between here + 1 and last. */
i = here;
while (++i <= last)
{
/* If the point is on the curve, then draw a line here from the
last control point. */
if (flags[i] & 01)
{
control1.x = x[i] * scale;
control1.y = y[i] * scale;
line_to (control1, dcontext);
/* Move to the next point. */
continue;
}
/* Off the curve points are more interesting. They are handled
one by one, with points in between being interpolated, until
either the last point is reached or an on-curve point is
processed. First, load the initial control points. */
control1.x = x[i] * scale;
control1.y = y[i] * scale;
while (++i <= last)
{
/* Load this point. */
control2.x = x[i] * scale;
control2.y = y[i] * scale;
/* If this point is on the curve, curve directly to this
point. */
if (flags[i] & 01)
{
curve_to (control1, control2, dcontext);
goto continue_loop;
}
/* Calculate the point between here and the previous
point. */
sfnt_lerp_half (&control1, &control2, &mid);
/* Curve over there. */
curve_to (control1, mid, dcontext);
/* Reload the control point. */
control1 = control2;
}
/* Close the contour by curving back to start. */
curve_to (control1, start, dcontext);
/* Don't close the contour twice. */
goto exit;
continue_loop:
continue;
}
/* Close the contour with a line back to start. */
line_to (start, dcontext);
exit:
return 0;
}
/* Decompose GLYPH into its individual components. Call MOVE_TO to
move to a specific location. For each line encountered, call
LINE_TO to draw a line to that location. For each spline
encountered, call CURVE_TO to draw the curves comprising the
spline.
If GLYPH is compound, use GET_GLYPH to obtain subglyphs. PROC must
return whether or not FREE_GLYPH will be called with the glyph
after sfnt_decompose_glyph is done with it. If GLYPH moreover
incorporates components whose anchor points are phantom points, use
GET_METRICS to obtain glyph metrics prerequisite for establishing
their coordinates.
When glyphs originate from a GX font with an active set of
transforms, the correct manner of applying such transforms is to
apply them within GET_GLYPH, while returning unaltered metrics from
GET_METRICS.
If there is a component glyph within GLYPH whose metrics have been
indicated as replacing those of its parent glyph, the variable
*METRICS_RETURN will be set to its metrics with GX-induced offsets
applied.
*METRICS_RETURN must initially hold metrics with GX offsets
applied, if any.
All functions will be called with DCONTEXT as an argument.
The winding rule used to fill the resulting lines is described in
chapter 2 of the TrueType reference manual, under the heading
"distinguishing the inside from the outside of a glyph."
Value is 0 upon success, or some non-zero value upon failure, which
can happen if the glyph is invalid. */
static int
sfnt_decompose_glyph (struct sfnt_glyph *glyph,
struct sfnt_glyph_metrics *metrics_return,
sfnt_move_to_proc move_to,
sfnt_line_to_proc line_to,
sfnt_curve_to_proc curve_to,
sfnt_get_glyph_proc get_glyph,
sfnt_free_glyph_proc free_glyph,
sfnt_get_metrics_proc get_metrics,
void *dcontext)
{
size_t here, last, n;
struct sfnt_compound_glyph_context context;
struct sfnt_glyph_metrics compound_metrics;
if (glyph->simple)
{
if (!glyph->number_of_contours)
/* No contours. Nothing needs to be decomposed. */
return 0;
here = 0;
for (n = 0; n < glyph->number_of_contours; ++n)
{
/* here is the first index into the glyph's point arrays
belonging to the contour in question. last is the index
of the last point in the contour. */
last = glyph->simple->end_pts_of_contours[n];
/* Make sure here and last make sense. */
if (here > last || last >= glyph->simple->number_of_points)
return 1;
/* Now perform the decomposition. */
if (sfnt_decompose_glyph_1 (here, last, move_to,
line_to, curve_to,
dcontext,
glyph->simple->x_coordinates,
glyph->simple->y_coordinates,
glyph->simple->flags,
65536))
return 1;
/* Move forward to the start of the next contour. */
here = last + 1;
}
return 0;
}
/* Decompose the specified compound glyph. */
memset (&context, 0, sizeof context);
/* Rather than handing METRICS_RETURN over to
sfnt_decompose_compound_glyph, save metrics within a temporary
variable and postpone returning them until it is certain the
decomposition has succeeded. */
compound_metrics = *metrics_return;
if (sfnt_decompose_compound_glyph (glyph, &context,
&compound_metrics,
get_glyph, free_glyph,
get_metrics, 0,
dcontext))
{
xfree (context.x_coordinates);
xfree (context.y_coordinates);
xfree (context.flags);
xfree (context.contour_end_points);
return 1;
}
*metrics_return = compound_metrics;
/* Now, generate the outlines. */
if (!context.num_end_points)
/* No contours. */
goto early;
here = 0;
for (n = 0; n < context.num_end_points; ++n)
{
/* here is the first index into the glyph's point arrays
belonging to the contour in question. last is the index
of the last point in the contour. */
last = context.contour_end_points[n];
/* Make sure here and last make sense. */
if (here > last || last >= context.num_points)
goto fail;
/* Now perform the decomposition. */
if (sfnt_decompose_glyph_2 (here, last, move_to,
line_to, curve_to,
dcontext,
context.x_coordinates,
context.y_coordinates,
context.flags, 1))
goto fail;
/* Move forward. */
here = last + 1;
}
early:
xfree (context.x_coordinates);
xfree (context.y_coordinates);
xfree (context.flags);
xfree (context.contour_end_points);
return 0;
fail:
xfree (context.x_coordinates);
xfree (context.y_coordinates);
xfree (context.flags);
xfree (context.contour_end_points);
return 1;
}
struct sfnt_build_glyph_outline_context
{
/* The outline being built. */
struct sfnt_glyph_outline *outline;
/* Factor to multiply positions by to get the pixel width. */
sfnt_fixed factor;
/* The position of the pen in 16.16 fixed point format. */
sfnt_fixed x, y;
};
/* Global state for sfnt_build_glyph_outline and related
functions. */
static struct sfnt_build_glyph_outline_context build_outline_context;
/* Append the given three words FLAGS, X, and Y to the outline
currently being built. Value is the new pointer to outline
memory. */
static struct sfnt_glyph_outline *
sfnt_build_append (int flags, sfnt_fixed x, sfnt_fixed y)
{
struct sfnt_glyph_outline *outline;
outline = build_outline_context.outline;
if (x == build_outline_context.x
&& y == build_outline_context.y
/* If the outline is presently empty, the first move_to must be
recorded even if its X and Y are set to origin. Without this
initial vertex, edges will be generated from the next vertex
onward, and thus be misaligned. */
&& outline->outline_used)
/* Ignore redundant motion. */
return build_outline_context.outline;
outline->outline_used++;
/* See if the outline has to be extended. Checking for overflow
should not be necessary. */
if (outline->outline_used > outline->outline_size)
{
outline->outline_size = outline->outline_used * 2;
/* Extend the outline to some size past the new size. */
outline = xrealloc (outline, (sizeof *outline
+ (outline->outline_size
* sizeof *outline->outline)));
outline->outline
= (struct sfnt_glyph_outline_command *) (outline + 1);
}
/* Write the outline data. */
outline->outline[outline->outline_used - 1].flags = flags;
outline->outline[outline->outline_used - 1].x = x;
outline->outline[outline->outline_used - 1].y = y;
/* Extend outline bounding box. */
if (outline->outline_used == 1)
{
/* These are the first points in the outline. */
outline->xmin = outline->xmax = x;
outline->ymin = outline->ymax = y;
}
else
{
outline->xmin = MIN ((sfnt_fixed) x, outline->xmin);
outline->ymin = MIN ((sfnt_fixed) y, outline->ymin);
outline->xmax = MAX ((sfnt_fixed) x, outline->xmax);
outline->ymax = MAX ((sfnt_fixed) y, outline->ymax);
}
return outline;
}
#ifndef INT64_MAX
/* 64 bit integer type. */
struct sfnt_large_integer
{
unsigned int high, low;
};
/* Calculate (A * B), placing the result in *VALUE. */
static void
sfnt_multiply_divide_1 (unsigned int a, unsigned int b,
struct sfnt_large_integer *value)
{
unsigned int lo1, hi1, lo2, hi2, lo, hi, i1, i2;
lo1 = a & 0x0000ffffu;
hi1 = a >> 16;
lo2 = b & 0x0000ffffu;
hi2 = b >> 16;
lo = lo1 * lo2;
i1 = lo1 * hi2;
i2 = lo2 * hi1;
hi = hi1 * hi2;
/* Check carry overflow of i1 + i2. */
i1 += i2;
hi += (unsigned int) (i1 < i2) << 16;
hi += i1 >> 16;
i1 = i1 << 16;
/* Check carry overflow of i1 + lo. */
lo += i1;
hi += (lo < i1);
value->low = lo;
value->high = hi;
}
/* Calculate AB / C. Value is a 32 bit unsigned integer. */
static unsigned int
sfnt_multiply_divide_2 (struct sfnt_large_integer *ab,
unsigned int c)
{
unsigned int hi, lo;
int i;
unsigned int r, q; /* Remainder and quotient. */
hi = ab->high;
lo = ab->low;
i = stdc_leading_zeros (hi);
r = (hi << i) | (lo >> (32 - i));
lo <<= i;
q = r / c;
r -= q * c;
i = 32 - i;
do
{
q <<= 1;
r = (r << 1) | (lo >> 31);
lo <<= 1;
if (r >= c)
{
r -= c;
q |= 1;
}
}
while (--i);
return q;
}
/* Add the specified unsigned 32-bit N to the large integer
INTEGER. */
static void
sfnt_large_integer_add (struct sfnt_large_integer *integer,
uint32_t n)
{
struct sfnt_large_integer number;
number.low = integer->low + n;
number.high = integer->high + (number.low
< integer->low);
*integer = number;
}
#endif /* !INT64_MAX */
/* Calculate (A * B) / C with no rounding and return the result, using
a 64 bit integer if necessary. */
static unsigned int
sfnt_multiply_divide (unsigned int a, unsigned int b, unsigned int c)
{
#ifndef INT64_MAX
struct sfnt_large_integer temp;
sfnt_multiply_divide_1 (a, b, &temp);
return sfnt_multiply_divide_2 (&temp, c);
#else /* INT64_MAX */
uint64_t temp;
temp = (uint64_t) a * (uint64_t) b;
return temp / c;
#endif /* !INT64_MAX */
}
/* Calculate (A * B) / C with rounding and return the result, using a
64 bit integer if necessary. */
static unsigned int
sfnt_multiply_divide_rounded (unsigned int a, unsigned int b,
unsigned int c)
{
#ifndef INT64_MAX
struct sfnt_large_integer temp;
sfnt_multiply_divide_1 (a, b, &temp);
sfnt_large_integer_add (&temp, c / 2);
return sfnt_multiply_divide_2 (&temp, c);
#else /* INT64_MAX */
uint64_t temp;
temp = (uint64_t) a * (uint64_t) b + c / 2;
return temp / c;
#endif /* !INT64_MAX */
}
#ifndef INT64_MAX
/* Calculate (A * B) / C, rounding the result with a threshold of N.
Use a 64 bit temporary. */
static unsigned int
sfnt_multiply_divide_round (unsigned int a, unsigned int b,
unsigned int n, unsigned int c)
{
struct sfnt_large_integer temp;
sfnt_multiply_divide_1 (a, b, &temp);
sfnt_large_integer_add (&temp, n);
return sfnt_multiply_divide_2 (&temp, c);
}
#endif /* !INT64_MAX */
/* The same as sfnt_multiply_divide_rounded, but handle signed values
instead. */
MAYBE_UNUSED static int
sfnt_multiply_divide_signed (int a, int b, int c)
{
int sign;
sign = 1;
if (a < 0)
sign = -sign;
if (b < 0)
sign = -sign;
if (c < 0)
sign = -sign;
return (sfnt_multiply_divide_rounded (abs (a), abs (b),
abs (c)) * sign);
}
/* Multiply the two 16.16 fixed point numbers X and Y. Return the
result regardless of overflow. */
static sfnt_fixed
sfnt_mul_fixed (sfnt_fixed x, sfnt_fixed y)
{
#ifdef INT64_MAX
int64_t product;
product = (int64_t) x * (int64_t) y;
/* This can be done quickly with int64_t. */
return product / (int64_t) 65536;
#else /* !INT64_MAX */
int sign;
sign = 1;
if (x < 0)
sign = -sign;
if (y < 0)
sign = -sign;
return sfnt_multiply_divide (abs (x), abs (y),
65536) * sign;
#endif /* INT64_MAX */
}
/* Multiply the two 16.16 fixed point numbers X and Y, with rounding
of the result. */
static sfnt_fixed
sfnt_mul_fixed_round (sfnt_fixed x, sfnt_fixed y)
{
#ifdef INT64_MAX
int64_t product, round;
product = (int64_t) x * (int64_t) y;
round = product < 0 ? -32768 : 32768;
/* This can be done quickly with int64_t. */
return (product + round) / (int64_t) 65536;
#else /* !INT64_MAX */
int sign;
sign = 1;
if (x < 0)
sign = -sign;
if (y < 0)
sign = -sign;
return sfnt_multiply_divide_round (abs (x), abs (y),
32768, 65536) * sign;
#endif /* INT64_MAX */
}
/* Set the pen size to the specified point and return. POINT will be
scaled up to the pixel size. */
static void
sfnt_move_to_and_build (struct sfnt_point point, void *dcontext)
{
sfnt_fixed x, y;
x = sfnt_mul_fixed (build_outline_context.factor, point.x);
y = sfnt_mul_fixed (build_outline_context.factor, point.y);
build_outline_context.outline = sfnt_build_append (0, x, y);
build_outline_context.x = x;
build_outline_context.y = y;
}
/* Record a line to the specified point and return. POINT will be
scaled up to the pixel size. */
static void
sfnt_line_to_and_build (struct sfnt_point point, void *dcontext)
{
sfnt_fixed x, y;
x = sfnt_mul_fixed (build_outline_context.factor, point.x);
y = sfnt_mul_fixed (build_outline_context.factor, point.y);
build_outline_context.outline
= sfnt_build_append (SFNT_GLYPH_OUTLINE_LINETO,
x, y);
build_outline_context.x = x;
build_outline_context.y = y;
}
/* Divide the two 16.16 fixed point numbers X and Y. Return the
result regardless of overflow. */
static sfnt_fixed
sfnt_div_fixed (sfnt_fixed x, sfnt_fixed y)
{
#ifdef INT64_MAX
int64_t result;
result = ((int64_t) x * 65536) / y;
return result;
#else
int sign;
unsigned int a, b;
sign = 1;
if (x < 0)
sign = -sign;
if (y < 0)
sign = -sign;
a = abs (x);
b = abs (y);
return sfnt_multiply_divide (a, 65536, b) * sign;
#endif
}
/* Return the ceiling value of the specified fixed point number X. */
static sfnt_fixed
sfnt_ceil_fixed (sfnt_fixed x)
{
return (x + 0177777) & 037777600000;
}
/* Return the floor value of the specified fixed point number X. */
static sfnt_fixed
sfnt_floor_fixed (sfnt_fixed x)
{
return x & 037777600000;
}
/* Given a curve consisting of three points CONTROL0, CONTROL1 and
ENDPOINT, return whether or not the curve is sufficiently small to
be approximated by a line between CONTROL0 and ENDPOINT. */
static bool
sfnt_curve_is_flat (struct sfnt_point control0,
struct sfnt_point control1,
struct sfnt_point endpoint)
{
struct sfnt_point g, h;
g.x = control1.x - control0.x;
g.y = control1.y - control0.y;
h.x = endpoint.x - control0.x;
h.y = endpoint.y - control0.y;
/* 1.0 is a constant representing the area covered at which point
the curve is considered "flat". */
return (abs (sfnt_mul_fixed (g.x, h.y)
- sfnt_mul_fixed (g.y, h.x))
<= 0200000);
}
/* Recursively split the splines in the bezier curve formed from
CONTROL0, CONTROL1 and ENDPOINT until the area between the curve's
two ends is small enough to be considered ``flat''. Then, turn
those ``flat'' curves into lines. */
static void
sfnt_curve_to_and_build_1 (struct sfnt_point control0,
struct sfnt_point control1,
struct sfnt_point endpoint)
{
struct sfnt_point ab, bc, abbc;
/* control0, control and endpoint make up the spline. Figure out
its distance from a line. */
if (sfnt_curve_is_flat (control0, control1, endpoint))
{
/* Draw a line to endpoint. */
build_outline_context.outline
= sfnt_build_append (SFNT_GLYPH_OUTLINE_LINETO,
endpoint.x, endpoint.y);
build_outline_context.x = endpoint.x;
build_outline_context.y = endpoint.y;
}
else
{
/* Calculate new control points.
Maybe apply a recursion limit here? */
sfnt_lerp_half (&control0, &control1, &ab);
sfnt_lerp_half (&control1, &endpoint, &bc);
sfnt_lerp_half (&ab, &bc, &abbc);
/* Keep splitting until a flat enough spline results. */
sfnt_curve_to_and_build_1 (control0, ab, abbc);
/* Then go on with the spline between control1 and endpoint. */
sfnt_curve_to_and_build_1 (abbc, bc, endpoint);
}
}
/* Scale and decompose the specified bezier curve into individual
lines. Then, record each of those lines into the outline being
built. */
static void
sfnt_curve_to_and_build (struct sfnt_point control,
struct sfnt_point endpoint,
void *dcontext)
{
struct sfnt_point control0;
control0.x = build_outline_context.x;
control0.y = build_outline_context.y;
control.x = sfnt_mul_fixed (control.x,
build_outline_context.factor);
control.y = sfnt_mul_fixed (control.y,
build_outline_context.factor);
endpoint.x = sfnt_mul_fixed (endpoint.x,
build_outline_context.factor);
endpoint.y = sfnt_mul_fixed (endpoint.y,
build_outline_context.factor);
sfnt_curve_to_and_build_1 (control0, control, endpoint);
}
/* Non-reentrantly build the outline for the specified GLYPH at the
given scale factor. Return the outline data with a refcount of 0
upon success, or NULL upon failure.
SCALE is a scale factor that converts between em space and device
space.
Use the unscaled glyph METRICS to determine the origin point of the
outline, or those of compound glyph components within *GLYPH
configured to replace their parents', which if existent are
returned in *METRICS. METRICS should not be altered by GX-derived
offsets, as they will be applied to *METRICS if present, following
this formula:
LBEARING = LBEARING - GLYPH->origin_distortion
ADVANCE = ADVANCE + GLYPH->advance_distortion
Call GET_GLYPH and FREE_GLYPH with the specified DCONTEXT to obtain
glyphs for compound glyph subcomponents, and GET_METRICS with the
provided DCONTEXT for unscaled glyph metrics. */
TEST_STATIC struct sfnt_glyph_outline *
sfnt_build_glyph_outline (struct sfnt_glyph *glyph,
sfnt_fixed scale,
struct sfnt_glyph_metrics *metrics,
sfnt_get_glyph_proc get_glyph,
sfnt_free_glyph_proc free_glyph,
sfnt_get_metrics_proc get_metrics,
void *dcontext)
{
struct sfnt_glyph_outline *outline;
int rc;
sfnt_fword origin;
memset (&build_outline_context, 0, sizeof build_outline_context);
/* Allocate the outline now with enough for 44 words at the end. */
outline = xmalloc (sizeof *outline + 40 * sizeof (*outline->outline));
outline->outline_size = 40;
outline->outline_used = 0;
outline->refcount = 0;
outline->outline
= (struct sfnt_glyph_outline_command *) (outline + 1);
/* DCONTEXT will be passed to GET_GLYPH and FREE_GLYPH, so global
variables must be used to communicate with the decomposition
functions. */
build_outline_context.outline = outline;
/* Clear outline bounding box. */
outline->xmin = 0;
outline->ymin = 0;
outline->xmax = 0;
outline->ymax = 0;
/* Set the scale factor. */
build_outline_context.factor = scale;
/* Apply the glyph's advance and origin distortion to METRICS in
advance of constructing the glyph outline, which might replace
METRICS with the metrics of a compound subglyph. */
metrics->lbearing -= glyph->origin_distortion;
metrics->advance += glyph->advance_distortion;
/* Decompose the outline. */
rc = sfnt_decompose_glyph (glyph, metrics,
sfnt_move_to_and_build,
sfnt_line_to_and_build,
sfnt_curve_to_and_build,
get_glyph, free_glyph, get_metrics,
dcontext);
/* Synchronize the outline object with what might have changed
inside sfnt_decompose_glyph. */
outline = build_outline_context.outline;
if (rc)
{
xfree (outline);
return NULL;
}
/* Compute the origin position. Note that the original glyph xmin
is first used to calculate the origin point, and the origin
distortion is applied to it to get the distorted origin. */
origin = glyph->xmin - metrics->lbearing;
outline->origin = sfnt_mul_fixed (origin, scale);
return outline;
}
/* Glyph rasterization. The algorithm used here is fairly simple.
Each contour is decomposed into lines, which turn into a polygon.
Then, a bog standard edge filler is used to turn them into
spans. */
/* Coverage table. This is a four dimensional array indiced by the Y,
then X axis fractional, shifted down to 2 bits. */
static const unsigned char sfnt_poly_coverage[8][9] =
{
{ 0, 4, 8, 12, 16, 20, 24, 28, 32, },
{ 0, 4, 8, 12, 16, 20, 24, 28, 32, },
{ 0, 4, 8, 12, 16, 20, 24, 28, 32, },
{ 0, 3, 7, 11, 15, 19, 23, 27, 31, },
{ 0, 4, 8, 12, 16, 20, 24, 28, 32, },
{ 0, 4, 8, 12, 16, 20, 24, 28, 32, },
{ 0, 4, 8, 12, 16, 20, 24, 28, 32, },
{ 0, 4, 8, 12, 16, 20, 24, 28, 32, },
};
/* Return the nearest coordinate on the sample grid no less than
F. */
static sfnt_fixed
sfnt_poly_grid_ceil (sfnt_fixed f)
{
return (((f + (SFNT_POLY_START - 1))
& ~(SFNT_POLY_STEP - 1)) + SFNT_POLY_START);
}
enum
{
SFNT_POLY_ALIGNMENT = 4,
};
/* Initialize the specified RASTER in preparation for displaying spans
for OUTLINE, and set RASTER->refcount to 0. The caller must then
set RASTER->cells to a zeroed array of size RASTER->stride *
RASTER->height, aligned to RASTER. */
TEST_STATIC void
sfnt_prepare_raster (struct sfnt_raster *raster,
struct sfnt_glyph_outline *outline)
{
raster->width
= (sfnt_ceil_fixed (outline->xmax)
- sfnt_floor_fixed (outline->xmin)) / 65536;
raster->height
= (sfnt_ceil_fixed (outline->ymax)
- sfnt_floor_fixed (outline->ymin)) / 65536;
raster->refcount = 0;
/* Align the raster to a SFNT_POLY_ALIGNMENT byte boundary. */
raster->stride = ((raster->width
+ (SFNT_POLY_ALIGNMENT - 1))
& ~(SFNT_POLY_ALIGNMENT - 1));
/* Apply outline->origin. This is 0 by convention in most fonts.
However, variable fonts typically change this as variations are
applied. */
raster->offx = sfnt_floor_fixed (outline->xmin
- outline->origin) / 65536;
raster->offy = sfnt_floor_fixed (outline->ymin) / 65536;
}
typedef void (*sfnt_edge_proc) (struct sfnt_edge *, size_t,
void *);
typedef void (*sfnt_span_proc) (struct sfnt_edge *, sfnt_fixed, void *);
/* Move EDGE->x forward, assuming that the scanline has moved upwards
by SFNT_POLY_STEP. */
static void
sfnt_step_edge (struct sfnt_edge *edge)
{
/* Add step. */
edge->x += edge->step_x;
}
/* Build a list of edges for each contour in OUTLINE, applying xmin
and ymin as the offset to each edge. Call EDGE_PROC with DCONTEXT
and the resulting edges as arguments. It is OK to modify the edges
given to EDGE_PROC. Align all edges to the sub-pixel grid. */
static void
sfnt_build_outline_edges (struct sfnt_glyph_outline *outline,
sfnt_edge_proc edge_proc, void *dcontext)
{
struct sfnt_edge *edges;
size_t i, edge, next_vertex;
sfnt_fixed dx, dy, bot, step_x, ymin, xmin;
size_t top, bottom, y;
edges = alloca (outline->outline_used * sizeof *edges);
edge = 0;
/* ymin and xmin must be the same as the offset used to set offy and
offx in rasters. */
ymin = sfnt_floor_fixed (outline->ymin);
xmin = sfnt_floor_fixed (outline->xmin);
for (i = 0; i < outline->outline_used; ++i)
{
/* Set NEXT_VERTEX to the next point (vertex) in this contour.
If i is past the end of the contour, then don't build edges
for this point. */
next_vertex = i + 1;
if (next_vertex == outline->outline_used
|| !(outline->outline[next_vertex].flags
& SFNT_GLYPH_OUTLINE_LINETO))
continue;
/* Skip past horizontal vertices. */
if (outline->outline[next_vertex].y == outline->outline[i].y)
continue;
/* Figure out the winding direction. */
if (outline->outline[next_vertex].y < outline->outline[i].y)
/* Vector will cross imaginary ray from its bottom from the
left of the ray. Winding is thus 1. */
edges[edge].winding = 1;
else
/* Moving clockwise. Winding is thus -1. */
edges[edge].winding = -1;
/* Figure out the top and bottom values of this edge. If the
next edge is below, top is here and bot is the edge below.
If the next edge is above, then top is there and this is the
bottom. */
if (outline->outline[next_vertex].y < outline->outline[i].y)
{
/* End of edge is below this one (keep in mind this is a
cartesian coordinate system, so smaller values are below
larger ones.) */
top = i;
bottom = next_vertex;
}
else
{
/* End of edge is above this one. */
bottom = i;
top = next_vertex;
}
bot = (outline->outline[bottom].y - ymin);
edges[edge].top = (outline->outline[top].y - ymin);
/* Record the edge. Rasterization happens from bottom to
up, so record the X at the bottom. */
edges[edge].x = (outline->outline[bottom].x - xmin);
dx = (outline->outline[top].x - outline->outline[bottom].x);
dy = abs (outline->outline[top].y
- outline->outline[bottom].y);
/* Step to first grid point. */
y = sfnt_poly_grid_ceil (bot);
/* If rounding would make the edge not cover any area, skip this
edge. */
if (y >= edges[edge].top)
continue;
/* Compute the step X. This is how much X changes for each
increase in Y. */
step_x = sfnt_div_fixed (dx, dy);
edges[edge].next = NULL;
/* Compute the step X scaled to the poly step. */
edges[edge].step_x
= sfnt_mul_fixed (step_x, SFNT_POLY_STEP);
/* Step to the grid point. */
edges[edge].x += sfnt_mul_fixed (step_x, bot - y);
/* Set the bottom position. */
edges[edge].bottom = y;
edge++;
}
if (edge)
edge_proc (edges, edge, dcontext);
}
/* Sort an array of SIZE edges to increase by bottom Y position, in
preparation for building spans.
Insertion sort is used because there are usually not very many
edges, and anything larger would bloat up the code. */
static void
sfnt_edge_sort (struct sfnt_edge *edges, size_t size)
{
ssize_t i, j;
struct sfnt_edge edge;
for (i = 1; i < size; ++i)
{
edge = edges[i];
j = i - 1;
while (j >= 0 && (edges[j].bottom > edge.bottom))
{
edges[j + 1] = edges[j];
j--;
}
edges[j + 1] = edge;
}
}
/* Draw EDGES, an unsorted array of polygon edges of size SIZE. For
each scanline, call SPAN_FUNC with a list of active edges and
coverage information, and DCONTEXT.
Sort each edge in ascending order by the bottommost Y coordinate to
which it applies. Start a loop on the Y coordinate, which starts
out at that of the bottommost edge. For each iteration, add edges
that now overlap with Y, keeping them sorted by X. Poly those
edges through SPAN_FUNC. Then, move upwards by SFNT_POLY_STEP,
remove edges that no longer apply, and interpolate the remaining
edges' X coordinates. Repeat until all the edges have been polyed.
Or alternatively, think of this as such: each edge is actually a
vector from its bottom position towards its top most position.
Every time Y moves upwards, the position of each edge intersecting
with Y is interpolated and added to a list of spans along with
winding information that is then given to EDGE_FUNC.
Anti-aliasing is performed using a coverage map for fractional
coordinates, and incrementing the Y axis by SFNT_POLY_STEP instead
of 1. SFNT_POLY_STEP is chosen to always keep Y aligned to a grid
placed such that there are always 1 << SFNT_POLY_SHIFT positions
available for each integral pixel coordinate. */
static void
sfnt_poly_edges (struct sfnt_edge *edges, size_t size,
sfnt_span_proc span_func, void *dcontext)
{
sfnt_fixed y;
size_t e;
struct sfnt_edge *active, **prev, *a, *n;
if (!size)
return;
/* Sort edges to ascend by Y-order. Once again, remember: cartesian
coordinates. */
sfnt_edge_sort (edges, size);
/* Step down line by line. Find active edges. */
y = edges[0].bottom;
active = NULL;
e = 0;
for (;;)
{
/* Add in new edges keeping them sorted. */
for (; e < size && edges[e].bottom <= y; ++e)
{
/* Find where to place this edge. */
for (prev = &active; (a = *prev); prev = &(a->next))
{
if (a->x > edges[e].x)
break;
}
edges[e].next = *prev;
*prev = &edges[e];
}
/* Draw this span at the current position. Y axis antialiasing
is expected to be handled by SPAN_FUNC. */
span_func (active, y, dcontext);
/* Compute the next Y position. */
y += SFNT_POLY_STEP;
/* Strip out edges that no longer have effect. */
for (prev = &active; (a = *prev);)
{
if (a->top <= y)
*prev = a->next;
else
prev = &a->next;
}
/* Break if all is done. */
if (!active && e == size)
break;
/* Step all edges. */
for (a = active; a; a = a->next)
sfnt_step_edge (a);
/* Resort on X axis. */
for (prev = &active; (a = *prev) && (n = a->next);)
{
if (a->x > n->x)
{
a->next = n->next;
n->next = a;
*prev = n;
prev = &active;
}
else
prev = &a->next;
}
}
}
/* Saturate and convert the given unsigned short value X to an
unsigned char. */
static unsigned char
sfnt_saturate_short (unsigned short x)
{
if (x > 255)
return 255;
return x;
}
/* Fill a single span of pixels between X0 and X1 at Y, a raster
coordinate, onto RASTER. */
static void
sfnt_fill_span (struct sfnt_raster *raster, sfnt_fixed y,
sfnt_fixed x0, sfnt_fixed x1)
{
unsigned char *start;
const unsigned char *coverage;
sfnt_fixed left, right, end;
unsigned short w, a;
int row;
#ifndef NDEBUG
unsigned char *row_end;
#endif /* NDEBUG */
/* Clip bounds to pixmap. */
if (x0 < 0)
x0 = 0;
/* If x1 is greater than the raster width, make sure the last pixel
is filled and no more after that. */
if (x1 > raster->width * 65536)
x1 = raster->width * 65536;
/* Check for empty spans. */
if (x1 <= x0)
return;
/* Figure out coverage based on Y axis fractional. */
coverage = sfnt_poly_coverage[(y >> (16 - SFNT_POLY_SHIFT))
& SFNT_POLY_MASK];
row = y >> 16;
/* Don't fill out of bounds rows. */
if (row < 0 || row >= raster->height)
return;
/* Set start, then start filling according to coverage. left and
right are now .3. */
left = x0 >> (16 - SFNT_POLY_SHIFT);
right = x1 >> (16 - SFNT_POLY_SHIFT);
start = raster->cells + row * raster->stride;
#ifndef NDEBUG
row_end = start + raster->width;
#endif /* NDEBUG */
start += left >> SFNT_POLY_SHIFT;
/* If left and right actually lie in the same pixel, just fill with
the coverage of both and return. */
if ((left & ~SFNT_POLY_MASK) == (right & ~SFNT_POLY_MASK))
{
/* Assert that start does not exceed the end of the row. */
assert (start <= row_end);
w = coverage[right - left];
a = *start + w;
*start = sfnt_saturate_short (a);
return;
}
/* Compute coverage for first pixel, then poly. The code from here
onwards assumes that left and right are on two different
pixels. */
if (left & SFNT_POLY_MASK)
{
/* Assert that start does not exceed the end of the row. */
assert (start <= row_end);
/* Compute the coverage for the first pixel, and move left past
it. The coverage is a number from 1 to 7 describing how
``partially'' covered this pixel is. */
end = (left + SFNT_POLY_SAMPLE - 1) & ~SFNT_POLY_MASK;
end = MIN (right, end);
w = coverage[end - left];
a = *start + w;
/* Now move left past. */
left = end;
*start++ = sfnt_saturate_short (a);
}
/* Clear coverage info for first pixel. Compute coverage for center
pixels. Note that SFNT_POLY_SAMPLE is used and not
SFNT_POLY_MASK, because coverage has a blank column at the
start. */
w = coverage[SFNT_POLY_SAMPLE];
/* Fill pixels between left and right. */
while (left + SFNT_POLY_MASK < right)
{
/* Assert that start does not exceed the end of the row. */
assert (start <= row_end);
a = *start + w;
*start++ = sfnt_saturate_short (a);
left += SFNT_POLY_SAMPLE;
}
/* Fill rightmost pixel with any partial coverage. */
if (right & SFNT_POLY_MASK)
{
/* Assert that start does not exceed the end of the row. */
assert (start <= row_end);
w = coverage[right - left];
a = *start + w;
*start = sfnt_saturate_short (a);
}
}
/* Poly each span starting from START onto RASTER, at position Y. Y
here is still a cartesian coordinate, where the bottom of the
raster is 0. But that is no longer true by the time sfnt_span_fill
is called. */
static void
sfnt_poly_span (struct sfnt_edge *start, sfnt_fixed y,
struct sfnt_raster *raster)
{
struct sfnt_edge *edge;
int winding;
sfnt_fixed x0, x1;
/* Pacify -Wmaybe-uninitialized; x1 and x0 are only used when edge
!= start, at which point x0 has already been set. */
x0 = x1 = 0;
/* Generate the X axis coverage map. Then poly it onto RASTER.
winding on each edge determines the winding direction: when it is
positive, winding is 1. When it is negative, winding is -1.
Fill each consecutive stretch of spans that are inside the glyph;
otherwise, coverage will overlap for some spans, but not
others.
The spans must be terminated with an edge that causes an
off-transition, or some spans will not be filled. */
winding = 0;
for (edge = start; edge; edge = edge->next)
{
if (!winding)
{
if (edge != start && x0 != x1)
/* Draw this section of spans that are on. */
sfnt_fill_span (raster, (raster->height << 16) - y,
x0, x1);
x0 = x1 = edge->x;
}
else
x1 = edge->x;
winding += edge->winding;
}
/* Draw the last span following the last off-transition. */
if (!winding && edge != start && x0 != x1)
sfnt_fill_span (raster, (raster->height << 16) - y,
x0, x1);
}
/* Main entry point for outline rasterization. */
/* Raster the spans between START and its end to the raster specified
as DCONTEXT. The span's position is Y. */
static void
sfnt_raster_span (struct sfnt_edge *start, sfnt_fixed y,
void *dcontext)
{
sfnt_poly_span (start, y, dcontext);
}
/* Generate and poly each span in EDGES onto the raster specified as
DCONTEXT. */
static void
sfnt_raster_edge (struct sfnt_edge *edges, size_t num_edges,
void *dcontext)
{
sfnt_poly_edges (edges, num_edges, sfnt_raster_span,
dcontext);
}
/* Generate an alpha mask for the glyph outline OUTLINE. Value is the
alpha mask upon success, NULL upon failure. */
TEST_STATIC struct sfnt_raster *
sfnt_raster_glyph_outline (struct sfnt_glyph_outline *outline)
{
struct sfnt_raster raster, *data;
/* Get the raster parameters. */
sfnt_prepare_raster (&raster, outline);
/* Allocate the raster data. */
data = xmalloc (sizeof *data + raster.stride * raster.height);
*data = raster;
data->cells = (unsigned char *) (data + 1);
memset (data->cells, 0, raster.stride * raster.height);
/* Generate edges for the outline, polying each array of edges to
the raster. */
sfnt_build_outline_edges (outline, sfnt_raster_edge, data);
/* All done. */
return data;
}
#define sfnt_add(a, b) \
((int) ((unsigned int) (a) + (unsigned int) (b)))
#define sfnt_sub(a, b) \
((int) ((unsigned int) (a) - (unsigned int) (b)))
#define sfnt_mul(a, b) \
((int) ((unsigned int) (a) * (unsigned int) (b)))
/* Exact coverage scaler.
The foregoing routines calculate partial coverage for each pixel by
increasing each span in increments finer than a single pixel, then
merging active spans into the raster.
Experience has proven this yields imperfect display results,
particularly when combined with glyph instruction code which aligns
points in a certain and as yet undetermined manner.
The scaler implemented in this page attains greater precision,
generating at length an array of scanlines, in which each is
represented by a list of steps. Each step holds an X coordinate
and a coverage value, which contributes to the coverage of each
pixel within the scanline rightwards or equal to the pixel with its
X coordinate.
Such a coverage value can be positive or negative; when the winding
direction of the span it derives from is positive, so is the
coverage value, that the pixels to its right (thus further into the
polygon it demarcates) might be painted in. In the other case, the
value is negative, thus negating the effect of preceding steps and
marking the outer boundary of the section of the polygon's
intersection with the scanline.
The procedure for producing this array of scanlines is largely an
adaptation of that which sfnt_poly_edges implements; in particular
the process of sorting and filtering edges remains untouched.
Rather than advancing through the edges SFNT_POLY_STEP at a time,
the edges are iterated over scanline-by-scanline. Every edge
overlapping with a particular scanline is considered piecemeal to
generate its array of steps.
An edge might overlap pixels within the scanline in one of four
fashions; each is illustrated with a graphic below:
+--------ee-----+------------------------------------------------+ (I)
| ee.......|................................................|
| ee.........|................................................|
| ee...........|................................................|
|ee.............|................................................|
ee---------------+------------------------------------------------+
In this instance, the edge partially overlaps its first pixel, but
the remainder all receive complete coverage.
+---------------+---------eeeeee+--------------------------------+ (II)
| | eeeeee......|................................|
| eeeee............|................................|
| eeeeee.|...............|................................|
| eeeeee.......|...............|................................|
eee-------------+---------------+--------------------------------+
In this instance, the edge partially overlaps two or more pixels on
this scanline. These pixels are referred to as a run.
+---------------+---------------+----------------+---------------+ (III)
| eeeeeee.|...............|................|...............|
| eeeeeeee..|...............|................|...............|
| eeeeeeee....|...............|................|...............|
| eeeeeee......|...............|................|...............|
+---------------+---------------+----------------+---------------+
This instance is much like the first instance, save that the
covered vertical area does not span the entire scanline.
+---------------+---------------+----------------+---------------+ (IV)
| | | | eeeeeeeeeee...|
| | eeeeeeeeeeeeeeeeeeeeeeeeeeeeeee...|
| eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee|...............|
|eeeeeeeeeeeeeeeeeeeeeee........|................|...............|
+---------------+---------------+----------------+---------------+
And this the second, again with the same distinction therefrom.
In each of these instances, a trapezoid is formed within every
pixel of the scanline, between:
- The point of the span's entry into the first pixel, either that
point itself or, for subsequent pixels, its projection onto
those pixels.
- The point of the span's exit or its termination.
- Both those points projected into the outer boundary of the
pertinent pixel.
The proportion formed by the area of this trapezoid and that of the
pixel then constitutes the coverage value to be recorded. */
/* Structure representing a step, as above. */
struct sfnt_step
{
/* The next step in this list. */
struct sfnt_step *next;
/* X coordinate of the step. This value affects all pixels at and
beyond this X coordinate. */
int x;
/* Coverage value between -1 and 1. */
float coverage;
};
/* Structure representing an array of steps, one for each
scanline. */
struct sfnt_step_raster
{
/* Number of scanlines within this raster. */
size_t scanlines;
/* Array of steps with one element for each scanline. */
struct sfnt_step **steps;
/* Linked list of chunks of steps allocated for this raster. */
struct sfnt_step_chunk *chunks;
};
enum
{
SFNT_BLOCK_STEPS = 128,
};
/* Structure representing a block of steps, which are allocated
SFNT_BLOCK_STEPS at a time. */
struct sfnt_step_chunk
{
/* The next chunk in this list, or NULL. */
struct sfnt_step_chunk *next;
/* Number of steps used within this chunk thus far. */
size_t nused;
/* The steps themselves. */
struct sfnt_step steps[SFNT_BLOCK_STEPS];
};
/* Structure representing an edge as consumed by the exact coverage
scaler. This structure is much like struct sfnt_edge, albeit with
all fractionals replaced by floating point numbers and an extra
field holding a Y delta. */
struct sfnt_fedge
{
/* Next edge in this chain. */
struct sfnt_fedge *next;
/* Winding direction. 1 if clockwise, -1 if counterclockwise. */
int winding;
/* X position, top and bottom of edges. */
float x, top, bottom;
/* Amount to move X by upon each change of Y, and vice versa. */
float step_x, step_y;
};
typedef void (*sfnt_fedge_proc) (struct sfnt_fedge *, size_t,
void *);
/* Build a list of edges for each contour in OUTLINE, displacing each
edge by xmin and ymin. Call EDGE_PROC with DCONTEXT and the edges
produced as arguments. */
static void
sfnt_build_outline_fedges (struct sfnt_glyph_outline *outline,
sfnt_fedge_proc edge_proc, void *dcontext)
{
struct sfnt_fedge *edges;
size_t i, edge, next_vertex;
sfnt_fixed dx, dy, step_x, step_y, ymin, xmin;
size_t top, bottom;
edges = alloca (outline->outline_used * sizeof *edges);
edge = 0;
/* ymin and xmin must be the same as the offset used to set offy and
offx in rasters. */
ymin = sfnt_floor_fixed (outline->ymin);
xmin = sfnt_floor_fixed (outline->xmin);
for (i = 0; i < outline->outline_used; ++i)
{
/* Set NEXT_VERTEX to the next point (vertex) in this contour.
If i is past the end of the contour, then don't build edges
for this point. */
next_vertex = i + 1;
if (next_vertex == outline->outline_used
|| !(outline->outline[next_vertex].flags
& SFNT_GLYPH_OUTLINE_LINETO))
continue;
/* Skip past horizontal vertices. */
if (outline->outline[next_vertex].y == outline->outline[i].y)
continue;
/* Figure out the winding direction. */
if (outline->outline[next_vertex].y < outline->outline[i].y)
/* Vector will cross imaginary ray from its bottom from the
left of the ray. Winding is thus 1. */
edges[edge].winding = 1;
else
/* Moving clockwise. Winding is thus -1. */
edges[edge].winding = -1;
/* Figure out the top and bottom values of this edge. If the
next edge is below, top is here and bot is the edge below.
If the next edge is above, then top is there and this is the
bottom. */
if (outline->outline[next_vertex].y < outline->outline[i].y)
{
/* End of edge is below this one (keep in mind this is a
cartesian coordinate system, so smaller values are below
larger ones.) */
top = i;
bottom = next_vertex;
}
else
{
/* End of edge is above this one. */
bottom = i;
top = next_vertex;
}
/* Record the edge. Rasterization happens from bottom to
up, so record the X at the bottom. */
dx = (outline->outline[top].x - outline->outline[bottom].x);
dy = abs (outline->outline[top].y
- outline->outline[bottom].y);
/* Compute the step X. This is how much X changes for each
increase in Y. */
step_x = sfnt_div_fixed (dx, dy);
/* And the step Y, which is the amount of movement to Y an
increase in X will incur. */
step_y = dx ? sfnt_div_fixed (dy, dx) : 0;
/* Save information computed above into the edge. */
edges[edge].top
= sfnt_fixed_float (outline->outline[top].y - ymin);
edges[edge].bottom
= sfnt_fixed_float (outline->outline[bottom].y - ymin);
edges[edge].x
= sfnt_fixed_float (outline->outline[bottom].x - xmin);
edges[edge].step_x = sfnt_fixed_float (step_x);
edges[edge].step_y = sfnt_fixed_float (step_y);
edges[edge].next = NULL;
/* Increment the edge index. */
edge++;
}
if (edge)
edge_proc (edges, edge, dcontext);
}
typedef void (*sfnt_step_raster_proc) (struct sfnt_step_raster *, void *);
/* Append a step with the supplied COVERAGE at X to the sorted list of
scanline steps within the container RASTER. Y is the scanline to
append to. */
static void
sfnt_insert_raster_step (struct sfnt_step_raster *raster,
int x, float coverage, size_t scanline)
{
struct sfnt_step_chunk *chunk;
struct sfnt_step *step, **p_next;
if (scanline >= raster->scanlines)
return;
if (x < 0)
x = 0;
/* Search within RASTER->steps[scanline] for a step at X. */
p_next = &raster->steps[scanline];
while ((step = *p_next))
{
if (step->x > x)
break;
if (step->x == x)
goto found;
p_next = &step->next;
}
if (!raster->chunks
|| raster->chunks->nused == SFNT_BLOCK_STEPS)
{
/* All chunks have been consumed, and consequently a new chunk
must be allocated. */
chunk = xmalloc (sizeof *chunk);
chunk->next = raster->chunks;
chunk->nused = 0;
raster->chunks = chunk;
}
else
chunk = raster->chunks;
step = &chunk->steps[chunk->nused++];
step->next = *p_next;
*p_next = step;
step->x = x;
step->coverage = 0;
found:
step->coverage += coverage;
}
/* Draw EDGES, an unsorted array of polygon edges of size NEDGES.
Transform EDGES into an array of steps representing a raster with
HEIGHT scanlines, then call POLY_FUNC with DCONTEXT and the
resulting struct sfnt_step_raster to transfer it onto an actual
raster.
WIDTH must be the width of the raster. Although there is no
guarantee that no steps generated extend past WIDTH, steps starting
after width might be omitted, and as such it must be accurate. */
static void
sfnt_poly_edges_exact (struct sfnt_fedge *edges, size_t nedges,
size_t height, size_t width,
sfnt_step_raster_proc proc, void *dcontext)
{
int y;
size_t size, e, edges_processed;
struct sfnt_fedge *active, **prev, *a, sentinel;
struct sfnt_step_raster raster;
struct sfnt_step_chunk *next, *last;
if (!height)
return;
/* Step down line by line. Find active edges. */
y = sfnt_floor_fixed (MAX (0, edges[0].bottom));
e = edges_processed = 0;
active = &sentinel;
/* Allocate the array of edges. */
raster.scanlines = height;
raster.chunks = NULL;
if (ckd_mul (&size, height, sizeof *raster.steps))
abort ();
raster.steps = xzalloc (size);
for (; y != height; y += 1)
{
/* Run over the whole array on each iteration of this loop;
experiments demonstrate this is faster for the majority of
glyphs. */
for (e = 0; e < nedges; ++e)
{
/* Although edges is unsorted, edges which have already been
processed will see their next fields set, and can thus be
disregarded. */
if (!edges[e].next
&& (edges[e].bottom < y + 1)
&& (edges[e].top > y))
{
/* As steps generated from each edge are sorted at the
time of their insertion, sorting the list of active
edges itself is redundant. */
edges[e].next = active;
active = &edges[e];
/* Increment the counter recording the number of edges
processed, which is used to terminate this loop early
once all have been processed. */
edges_processed++;
}
}
/* Iterate through each active edge, appending steps for it, and
removing it if it does not overlap with the next
scanline. */
for (prev = &active; (a = *prev) != &sentinel;)
{
float x_top, x_bot, x_min, x_max;
float y_top, y_bot;
int x_pixel_min, x_pixel_max;
#define APPEND_STEP(x, coverage) \
sfnt_insert_raster_step (&raster, x, coverage, y);
/* Calculate several values to establish which overlap
category this edge falls into. */
y_top = y + 1; /* Topmost coordinate covered by this
edge in this scanline. */
y_bot = y; /* Bottom-most coordinate covered by this
edge in this scanline. */
/* III or IV? If the edge terminates before the next
scanline, make its terminus y_top. */
if (y_top > a->top)
y_top = a->top;
/* Same goes for y_bottom. */
if (a->bottom > y_bot)
y_bot = a->bottom;
/* y_top should never equal y_bottom, but check to be on the
safe side. */
if (y_top == y_bot)
goto next;
/* x_top and x_bot are the X positions where the edge enters
and exits this scanline. */
/*
(x_top)
+--------ee-----+------------------------------------------------+ (y_top)
| ee.......|................................................|
| ee.........|................................................|
| ee...........|................................................|
|ee.............|................................................|
ee---------------+------------------------------------------------+ (y_bot)
(x_bot)
(y_bot might be further below.)
*/
x_top = (y_top - a->bottom) * a->step_x + a->x;
x_bot = (y_bot - a->bottom) * a->step_x + a->x;
x_min = MIN (x_top, x_bot);
x_max = MAX (x_top, x_bot);
/* Pixels containing x_bot and x_top respectively. */
x_pixel_min = (int) (x_min);
x_pixel_max = (int) (x_max);
#define TRAPEZOID_AREA(height, top_start, top_end, bot_start, bot_end) \
((((float) (top_end) - (top_start)) \
+ ((float) (bot_end) - (bot_start))) \
/ 2.0f * (float) (height))
/* I, III? These two instances' criteria are that the edge
enters and exits within one pixel. */
if (x_pixel_min == x_pixel_max)
{
float xmin, xmax, ytop, ybot, height;
float coverage, delta;
/* Partial coverage for the first pixel. */
xmin = (x_min);
xmax = (x_max);
ytop = (y_top);
ybot = (y_bot);
height = ytop - ybot;
/* The trapezoid here is one of the following two:
ytop+------xmax--+-----------+---------------------------------------------+
| /................................................................|
| /.......|...........|.............................................|
| /........|...........|.............................................|
| /.........|...........|.............................................|
| / ...................................................................|
xmin+------------+-----------+---------------------------------------------+
ybot
ytop+------------+-----------+---------------------------------------------+
|\ xmin................................................................|
| \..........|...........|.............................................|
| \.........|...........|.............................................|
| \........|...........|.............................................|
| \.......|...........|.............................................|
| \................................................................|
+------xmax--+-----------+---------------------------------------------+
In either situation, the first pixel's coverage is
the space occupied by a trapezoid whose corners are
xmin and x_pixel_min + 1 and xmax and x_pixel_min +
1, and whose height is ytop - ybot. The coverage for
the remainder is the height alone. */
coverage = (TRAPEZOID_AREA (height,
xmin, (int) xmin + 1,
xmax, (int) xmax + 1)
* a->winding);
APPEND_STEP (x_pixel_min, coverage);
/* Then if the next pixel isn't beyond the raster,
append complete coverage for it. */
if (x_pixel_min + 1 < width)
{
delta = (y_top - y_bot) * a->winding;
APPEND_STEP (x_pixel_max + 1, delta - coverage);
}
}
else
{
float dy, y_crossing, coverage;
float ytop, ybot, xtop, xbot, increment;
float x, last, here;
ytop = (y_top);
ybot = (y_bot);
xtop = (x_top);
xbot = (x_bot);
#define TRIANGLE_AREA(width, height) \
((width) * (height) / 2.0f)
/* II, IV. Coverage must be computed for each pixel
from x_pixel_min to x_pixel_max, with the latter
treated much as in I or III. */
if (x_bot < x_top)
{
/*
y_top x_top
+-----------+-----------+-----------+------------+-------/------+-------------------------------+
| | | | | /--.......|...............................|
|x_pixel_min| | | | /--..........|...............................|
| | | | /+-y_crossing...|...............................|
| | | | /--.|..............|...............................|
| | | | /-....|..............|...............................|
| | | | /--......|..x_pixel_max.|...............................|
| | | |/--.........|..............|...............................|
| | | /-+............|..............|...............................|
| | | /--..|............|..............|...............................|
| | | /--.....|............|..............|...............................|
| | |/--........|............|..............|...............................|
| | /-+...........|............|..............|...............................|
| | /--..|...........|............|..............|...............................|
| | /--.....|...........|............|..............|...............................|
| | /-........|...........|............|..............|...............................|
| /+-y_crossing|...........|............|..............|...............................|
| /--.|...........|...........|............|..............|...............................|
| /--....|...........|...........|............|..............|...............................|
| /--.......|...........|...........|............|..............|...............................|
+-----------+-----------+-----------+------------+--------------+-------------------------------+
y_bot x_bot
The purpose of this code is to calculate the area occupied by dots of
each pixel in between x_pixel_min and x_pixel_max + 1.
The area occupied in the first pixel is a triangle comprising [x_bot,
y_bot], [x_bot + 1, y_bot], and [x_bot + 1, y_crossing].
The area occupied in the second pixel through x_pixel_max - 1 is that
of a rectangle comprising [y_bot, pixel], [the previous rectangle's
y_crossing, pixel], [the previous rectangle's y_crossing, pixel + 1],
and [pixel + 1, y_bot] summed with the area the remaining triangle.
The area occupied in the last pixel is a trapezoid proper.
Thus the procedure is roughly as follows: dy is computed, which is the
increase to the Y of the edge for each increase in scanline X. */
dy = a->step_y;
/* As is y_crossing for the first pixel. */
y_crossing = ybot + dy * ((int) xbot + 1 - xbot);
/* And the area of the first triangle.
The width is (int) xbot + 1 - xbot, and the
height is y_crossing - ybot. */
last = ((TRIANGLE_AREA (y_crossing - ybot,
(int) xbot + 1 - xbot))
* a->winding);
APPEND_STEP (x_pixel_min, last);
/* Coverage value for subsequent rectangles. The
value set here is for the next pixel, which is
filled from ybot to y_crossing. */
coverage = (y_crossing - ybot) * a->winding;
increment = dy * a->winding;
for (x = x_pixel_min + 1; x < x_pixel_max; x++)
{
here = coverage + increment / 2;
APPEND_STEP (x, here - last);
last = here;
coverage += increment;
}
/* The y_crossing for the last pixel. */
y_crossing = ybot + dy * ((int) xtop - xbot);
/* And calculate the area of the trapezoid in the
last pixel. */
coverage += a->winding * TRAPEZOID_AREA (ytop - y_crossing,
xtop,
(int) xtop + 1,
(int) xtop,
(int) xtop + 1);
here = coverage;
APPEND_STEP (x_pixel_max, here - last);
last = here;
/* Fill the remainder of the scanline with
height-derived coverage. */
if (x_pixel_max + 1 < width)
APPEND_STEP (x_pixel_max + 1, ((y_top - y_bot)
* a->winding - last));
}
else /* if (x_bot > x_top) */
{
/*
y_top x_top
+----------------+----------------+-----------------+-----------------+-----------------------------+
| \--........|................|.................|.................|.............................|
| \--.....|................|.................|.................|.............................|
| \--..|................|.................|.................|.............................|
| \-+.y_crossing.....|.................|.................|.............................|
| |\--.............|.................|.................|.............................|
| | \--..........|.................|.................|.............................|
| x_pixel_min | \---......|.................|.................|.............................|
| | \--...|.................|.................|.............................|
| | \--|y_crossing.......|.................|.............................|
| | \--...............|.................|.............................|
| | | \--............|.................|.............................|
| | | \--.........|.................|.............................|
| | | \--......|.................|.............................|
+----------------+----------------+-----------\-----+-----------------+-----------------------------+
y_bot x_bot
Whereas in this situation the trapezoid is inverted, and the code must
be as well. */
/* The edge's Y decreases as the edge's X increases,
yielding a negative a->step_x. */
dy = a->step_y;
/* Calculate y_crossing for the first pixel. */
y_crossing = ytop + dy * ((int) xtop + 1 - xtop);
/* And the area of the first triangle. */
last = ((TRIANGLE_AREA ((int) xtop + 1 - xtop,
ytop - y_crossing))
* a->winding);
APPEND_STEP (x_pixel_min, last);
/* Coverage value for subsequent rectangles. The
value set here is for the next pixel, which is
filled from ytop to y_crossing. */
coverage = (ytop - y_crossing) * a->winding;
increment = -dy * a->winding;
for (x = x_pixel_min + 1; x < x_pixel_max; x ++)
{
here = coverage + increment / 2;
APPEND_STEP (x, here - last);
last = here;
coverage += increment;
}
/* The y_crossing for the last pixel. */
y_crossing = ytop + dy * ((int) xbot - xtop);
/* And calculate the area of the trapezoid in the
last pixel. */
coverage += a->winding * TRAPEZOID_AREA (y_crossing - ybot,
(int) xbot,
(int) xbot + 1,
xbot,
(int) xbot + 1);
here = coverage;
APPEND_STEP (x_pixel_max, here - last);
last = here;
/* Fill the remainder of the scanline with
height-derived coverage. */
if (x_pixel_max + 1 < width)
APPEND_STEP (x_pixel_max + 1, ((y_top - y_bot)
* a->winding - last));
}
#undef TRIANGLE_AREA
}
#undef APPEND_STEP
#undef TRAPEZOID_AREA
/* When an edge is created, its a->bottom (and by extension
a->y) is not aligned to a->x. Since this iteration can
only affect the scan line Y, align a to the next
scanline, that the next iteration of this loop to
consider it might consider its entire intersection. */
a->x += a->step_x * (y + 1 - a->bottom);
a->bottom = y + 1;
next:
if (a->top < y + 1)
*prev = a->next;
else
/* This edge doesn't intersect with the next scanline;
remove it from the list. After the edge at hand is so
deleted from the list, its next field remains set,
excluding it from future consideration. */
prev = &a->next;
}
/* Break if all is done. */
if (active == &sentinel && edges_processed == nedges)
break;
}
(*proc) (&raster, dcontext);
xfree (raster.steps);
/* Free each block of steps allocated. */
next = raster.chunks;
while (next)
{
last = next;
next = next->next;
xfree (last);
}
}
/* Apply winding rule to the coverage value VALUE. Convert VALUE to a
number between 0 and 255. If VALUE is negative, invert it. If it
exceeds 255 afterwards, truncate it to 255. */
static int
sfnt_compute_fill (float value)
{
if (value < 0)
value = -value;
return MIN (value * 255, 255);
}
/* Set N pixels at DATA to the value VALUE. If N is large, call
memset; otherwise set this by hand. */
static void
sfnt_poly_set_steps (unsigned char *data, int value, int n)
{
unsigned char *p;
p = data;
switch (n)
{
case 7:
*p++ = value;
FALLTHROUGH;
case 6:
*p++ = value;
FALLTHROUGH;
case 5:
*p++ = value;
FALLTHROUGH;
case 4:
*p++ = value;
FALLTHROUGH;
case 3:
*p++ = value;
FALLTHROUGH;
case 2:
*p++ = value;
FALLTHROUGH;
case 1:
*p++ = value;
FALLTHROUGH;
case 0:
break;
default:
memset (data, value, n);
}
}
/* Transfer steps generated by sfnt_poly_edges_exact from STEPS to the
provided raster RASTER. */
static void
sfnt_poly_steps (struct sfnt_step_raster *steps,
struct sfnt_raster *raster)
{
int y;
unsigned char *data;
int x, xend, fill;
float total;
struct sfnt_step *step;
y = 0; /* This y is an X-style coordinate in RASTER's space.
Its counterpart array of steps is STEPS->steps[
raster->height - y - 1]. */
data = raster->cells;
for (y = 0; y < raster->height; ++y, data += raster->stride)
{
fill = total = x = 0;
for (step = steps->steps[raster->height - y - 1];
step && x < raster->width; step = step->next)
{
xend = MIN (step->x, raster->width);
if (fill)
sfnt_poly_set_steps (data + x, fill, xend - x);
total += step->coverage;
fill = sfnt_compute_fill (total);
x = xend;
}
if (x < raster->width)
sfnt_poly_set_steps (data + x, fill, raster->width - x);
}
}
/* Poly each edge in EDGES onto the raster supplied in DCONTEXT. */
static void
sfnt_raster_steps (struct sfnt_step_raster *steps, void *dcontext)
{
sfnt_poly_steps (steps, dcontext);
}
/* Call sfnt_poly_edges_exact with suitable arguments for polying
EDGES onto DCONTEXT, a raster structure. */
static void
sfnt_raster_edges_exact (struct sfnt_fedge *edges, size_t size,
void *dcontext)
{
struct sfnt_raster *raster;
raster = dcontext;
sfnt_poly_edges_exact (edges, size, raster->height,
raster->width, sfnt_raster_steps,
dcontext);
}
/* Generate an alpha mask for the glyph outline OUTLINE by means of
the exact coverage scaler. Value is the alpha mask upon success,
NULL upon failure. */
TEST_STATIC struct sfnt_raster *
sfnt_raster_glyph_outline_exact (struct sfnt_glyph_outline *outline)
{
struct sfnt_raster raster, *data;
/* Get the raster parameters. */
sfnt_prepare_raster (&raster, outline);
/* Allocate the raster data. */
data = xmalloc (sizeof *data + raster.stride * raster.height);
*data = raster;
data->cells = (unsigned char *) (data + 1);
memset (data->cells, 0, raster.stride * raster.height);
/* Generate edges for the outline, polying each array of edges to
the raster. */
sfnt_build_outline_fedges (outline, sfnt_raster_edges_exact, data);
/* All done. */
return data;
}
/* Glyph metrics computation. */
/* Read an hmtx table from the font FD, using the table directory
specified as SUBTABLE, the maxp table MAXP, and the hhea table
HHEA.
Return NULL upon failure, and the hmtx table otherwise.
HHEA->num_of_long_hor_metrics determines the number of horizontal
metrics present, and MAXP->num_glyphs -
HHEA->num_of_long_hor_metrics determines the number of left-side
bearings present. */
TEST_STATIC struct sfnt_hmtx_table *
sfnt_read_hmtx_table (int fd, struct sfnt_offset_subtable *subtable,
struct sfnt_hhea_table *hhea,
struct sfnt_maxp_table *maxp)
{
struct sfnt_table_directory *directory;
struct sfnt_hmtx_table *hmtx;
size_t size;
ssize_t rc;
int i;
/* Find the table in the directory. */
directory = sfnt_find_table (subtable, SFNT_TABLE_HMTX);
if (!directory)
return NULL;
/* Figure out how many bytes are required. */
size = ((hhea->num_of_long_hor_metrics
* sizeof (struct sfnt_long_hor_metric))
+ (MAX (0, ((int) maxp->num_glyphs
- hhea->num_of_long_hor_metrics))
* sizeof (int16_t)));
/* Check the length matches exactly. */
if (directory->length != size)
return NULL;
/* Seek to the location given in the directory. */
if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
return NULL;
/* Now allocate enough to hold all of that along with the table
directory structure. */
hmtx = xmalloc (sizeof *hmtx + size);
/* Read into hmtx + 1. */
rc = read (fd, hmtx + 1, size);
if (rc == -1 || rc < size)
{
xfree (hmtx);
return NULL;
}
/* Set pointers to data. */
hmtx->h_metrics = (struct sfnt_long_hor_metric *) (hmtx + 1);
hmtx->left_side_bearing
= (int16_t *) (hmtx->h_metrics
+ hhea->num_of_long_hor_metrics);
/* Swap what was read. */
for (i = 0; i < hhea->num_of_long_hor_metrics; ++i)
{
sfnt_swap16 (&hmtx->h_metrics[i].advance_width);
sfnt_swap16 (&hmtx->h_metrics[i].left_side_bearing);
}
for (; i < maxp->num_glyphs; ++i)
sfnt_swap16 (&hmtx->left_side_bearing[i - hhea->num_of_long_hor_metrics]);
/* All done. */
return hmtx;
}
/* Obtain unscaled glyph metrics for the glyph indexed by GLYPH.
Return 0 and the metrics in *METRICS if metrics could be found,
else 1.
HMTX, HHEA and MAXP should be the hmtx, hhea, head, and maxp tables
of the font respectively. */
TEST_STATIC int
sfnt_lookup_glyph_metrics (sfnt_glyph glyph,
struct sfnt_glyph_metrics *metrics,
struct sfnt_hmtx_table *hmtx,
struct sfnt_hhea_table *hhea,
struct sfnt_maxp_table *maxp)
{
short lbearing;
unsigned short advance;
if (glyph < hhea->num_of_long_hor_metrics)
{
/* There is a long entry in the hmtx table. */
lbearing = hmtx->h_metrics[glyph].left_side_bearing;
advance = hmtx->h_metrics[glyph].advance_width;
}
else if (hhea->num_of_long_hor_metrics
&& glyph < maxp->num_glyphs)
{
/* There is a short entry in the hmtx table. */
lbearing
= hmtx->left_side_bearing[glyph
- hhea->num_of_long_hor_metrics];
advance
= hmtx->h_metrics[hhea->num_of_long_hor_metrics - 1].advance_width;
}
else
/* No entry corresponds to the glyph. */
return 1;
/* Return unscaled metrics. */
metrics->lbearing = lbearing;
metrics->advance = advance;
return 0;
}
/* Scale the specified glyph metrics by FACTOR. Set METRICS->lbearing
and METRICS->advance to their current values times factor; take the
floor of the left bearing and round the advance width. */
MAYBE_UNUSED TEST_STATIC void
sfnt_scale_metrics (struct sfnt_glyph_metrics *metrics,
sfnt_fixed factor)
{
sfnt_fixed lbearing, advance;
lbearing = sfnt_mul_fixed (metrics->lbearing * 65536, factor);
advance = sfnt_mul_fixed (metrics->advance * 65536, factor);
metrics->lbearing = sfnt_floor_fixed (lbearing);
metrics->advance = sfnt_round_fixed (advance);
}
/* Calculate the factor used to convert em space to device space for a
font with the specified HEAD table and PPEM value. */
MAYBE_UNUSED TEST_STATIC sfnt_fixed
sfnt_get_scale (struct sfnt_head_table *head, int ppem)
{
/* Figure out how to convert from font unit-space to pixel space.
To turn one unit to its corresponding pixel size given a ppem of
1, the unit must be divided by head->units_per_em. Then, it must
be multiplied by the ppem. So,
PIXEL = UNIT / UPEM * PPEM
which means:
PIXEL = UNIT * PPEM / UPEM */
return sfnt_div_fixed (ppem, head->units_per_em);
}
/* Font style parsing. */
/* Read the name table from the given font FD, using the table
directory specified as SUBTABLE. Perform validation on the offsets
in the name records. Return NULL upon failure, else the name
table. */
TEST_STATIC struct sfnt_name_table *
sfnt_read_name_table (int fd, struct sfnt_offset_subtable *subtable)
{
struct sfnt_table_directory *directory;
struct sfnt_name_table *name;
size_t required;
ssize_t rc;
int i;
/* Find the table in the directory. */
directory = sfnt_find_table (subtable, SFNT_TABLE_NAME);
if (!directory)
return NULL;
/* Seek to the location given in the directory. */
if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
return NULL;
/* Figure out the minimum that has to be read. */
required = SFNT_ENDOF (struct sfnt_name_table,
string_offset, uint16_t);
if (directory->length < required)
return NULL;
/* Allocate enough to hold the name table and variable length
data. */
name = xmalloc (sizeof *name + directory->length);
/* Read the fixed length data. */
rc = read (fd, name, required);
if (rc == -1 || rc < required)
{
xfree (name);
return NULL;
}
/* Swap what was read. */
sfnt_swap16 (&name->format);
sfnt_swap16 (&name->count);
sfnt_swap16 (&name->string_offset);
/* Reject unsupported formats. */
if (name->format)
{
xfree (name);
return NULL;
}
/* Set the pointer to the start of the variable length data. */
name->name_records
= (struct sfnt_name_record *) (name + 1);
/* Check there is enough for the name records. */
required = directory->length - required;
if (required < name->count * sizeof *name->name_records)
{
xfree (name);
return NULL;
}
/* Read the variable length data. First, read the name records. */
rc = read (fd, name->name_records,
(name->count
* sizeof *name->name_records));
if (rc == -1 || (rc < (name->count
* sizeof *name->name_records)))
{
xfree (name);
return NULL;
}
/* Swap each of the name records. */
for (i = 0; i < name->count; ++i)
{
sfnt_swap16 (&name->name_records[i].platform_id);
sfnt_swap16 (&name->name_records[i].platform_specific_id);
sfnt_swap16 (&name->name_records[i].language_id);
sfnt_swap16 (&name->name_records[i].name_id);
sfnt_swap16 (&name->name_records[i].length);
sfnt_swap16 (&name->name_records[i].offset);
}
/* Now, read the name data. */
if (name->string_offset > directory->length)
{
xfree (name);
return NULL;
}
required = directory->length - name->string_offset;
/* It can happen that the string offset comes before the name
records, and as a result exceeds the number of bytes
previously allocated. Extend name if that is the case. */
if (required > (directory->length
- (name->count
* sizeof *name->name_records)))
{
name = xrealloc (name, (sizeof *name
+ (name->count
* sizeof *name->name_records)
+ required));
name->name_records = (struct sfnt_name_record *) (name + 1);
}
/* There is enough space past name->name_records to hold REQUIRED
bytes. Seek to the right offset. */
if (lseek (fd, directory->offset + name->string_offset,
SEEK_SET) == (off_t) -1)
{
xfree (name);
return NULL;
}
/* Read REQUIRED bytes into the string data. */
name->data = (unsigned char *) (name->name_records
+ name->count);
rc = read (fd, name->data, required);
if (rc == -1 || rc < required)
{
xfree (name);
return NULL;
}
/* Now validate each of the name records. */
for (i = 0; i < name->count; ++i)
{
if (((int) name->name_records[i].offset
+ name->name_records[i].length) > required)
{
/* The name is out of bounds! */
xfree (name);
return NULL;
}
}
/* Return the name table. */
return name;
}
/* Return a pointer to the name data corresponding with CODE under the
name table NAME. Return the start of the data and the name record
under *RECORD upon success, and NULL otherwise. */
TEST_STATIC unsigned char *
sfnt_find_name (struct sfnt_name_table *name,
enum sfnt_name_identifier_code code,
struct sfnt_name_record *record)
{
int i;
for (i = 0; i < name->count; ++i)
{
if (name->name_records[i].name_id == code)
{
/* The offsets within have already been validated. */
*record = name->name_records[i];
return name->data + record->offset;
}
}
return NULL;
}
/* Read the meta table from the given font FD, using the table
directory specified as SUBTABLE. Perform validation on the offsets
in each metadata record. Return NULL upon failure, else the meta
table. */
TEST_STATIC struct sfnt_meta_table *
sfnt_read_meta_table (int fd, struct sfnt_offset_subtable *subtable)
{
struct sfnt_table_directory *directory;
struct sfnt_meta_table *meta;
size_t required, i, data_size, map_size, offset;
ssize_t rc;
/* Find the table in the directory. */
directory = sfnt_find_table (subtable, SFNT_TABLE_META);
if (!directory)
return NULL;
/* Seek to the location given in the directory. */
if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
return NULL;
/* Figure out the minimum that has to be read. */
required = SFNT_ENDOF (struct sfnt_meta_table,
num_data_maps, uint32_t);
if (directory->length < required)
return NULL;
/* Allocate enough to hold it. */
meta = xmalloc (sizeof *meta);
/* Read the header. */
rc = read (fd, meta, required);
if (rc == -1 || rc < required)
{
xfree (meta);
return NULL;
}
/* Swap what has been read so far. */
sfnt_swap32 (&meta->version);
sfnt_swap32 (&meta->flags);
sfnt_swap32 (&meta->data_offset);
sfnt_swap32 (&meta->num_data_maps);
/* Make sure the meta is supported. */
if (meta->version != 1)
{
xfree (meta);
return NULL;
}
/* Reallocate the table to hold sizeof *meta + meta->num_data_maps
times sizeof meta->data_maps + directory->length bytes. This is
because it is ok for metadata to point into the data map itself,
so an unswapped copy of the whole meta contents must be
retained. */
if (ckd_mul (&map_size, sizeof *meta->data_maps, meta->num_data_maps)
/* Do so while checking for overflow from bad sfnt files. */
|| ckd_add (&data_size, map_size, sizeof *meta)
|| ckd_add (&data_size, data_size, directory->length))
{
xfree (meta);
return NULL;
}
/* Do the reallocation. */
meta = xrealloc (meta, data_size);
/* Check that the remaining data is big enough to hold the data
maps. */
if (directory->length - required < map_size)
{
xfree (meta);
return NULL;
}
/* Set pointers to data_maps and data. */
meta->data_maps = (struct sfnt_meta_data_map *) (meta + 1);
meta->data = (unsigned char *) (meta->data_maps
+ meta->num_data_maps);
/* Now, seek back. Read the entire table into meta->data. */
if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
{
xfree (meta);
return NULL;
}
rc = read (fd, meta->data, directory->length);
if (rc < directory->length)
{
xfree (meta);
return NULL;
}
/* Copy the data maps into meta->data_maps and swap them one by
one. */
memcpy (meta->data_maps, meta->data + required,
map_size);
for (i = 0; i < meta->num_data_maps; ++i)
{
sfnt_swap32 (&meta->data_maps[i].tag);
sfnt_swap32 (&meta->data_maps[i].data_offset);
sfnt_swap32 (&meta->data_maps[i].data_length);
/* Verify the data offsets. Overflow checking is particularly
important here. */
if (ckd_add (&offset, meta->data_maps[i].data_offset,
meta->data_maps[i].data_length))
{
xfree (meta);
return NULL;
}
if (offset > directory->length)
{
xfree (meta);
return NULL;
}
}
/* All done. */
return meta;
}
/* Return a pointer to the metadata corresponding to TAG under the
meta table META. Return the start of the data and the metadata map
under *MAP upon success, and NULL otherwise. */
MAYBE_UNUSED TEST_STATIC char *
sfnt_find_metadata (struct sfnt_meta_table *meta,
enum sfnt_meta_data_tag tag,
struct sfnt_meta_data_map *map)
{
int i;
for (i = 0; i < meta->num_data_maps; ++i)
{
if (meta->data_maps[i].tag == tag)
{
*map = meta->data_maps[i];
return (char *) meta->data + map->data_offset;
}
}
return NULL;
}
/* TrueType collection format support. */
/* Read a TrueType collection header from the font file FD.
FD must currently at the start of the file.
Value is the header upon success, else NULL. */
TEST_STATIC struct sfnt_ttc_header *
sfnt_read_ttc_header (int fd)
{
struct sfnt_ttc_header *ttc;
size_t size, i;
ssize_t rc;
/* First, allocate only as much as required. */
ttc = xmalloc (sizeof *ttc);
/* Read the version 1.0 data. */
size = SFNT_ENDOF (struct sfnt_ttc_header, num_fonts,
uint32_t);
rc = read (fd, ttc, size);
if (rc == -1 || rc < size)
{
xfree (ttc);
return NULL;
}
/* Now swap what was read. */
sfnt_swap32 (&ttc->ttctag);
sfnt_swap32 (&ttc->version);
sfnt_swap32 (&ttc->num_fonts);
/* Verify that the tag is as expected. */
if (ttc->ttctag != SFNT_TTC_TTCF)
{
xfree (ttc);
return NULL;
}
/* Now, read the variable length data. Make sure to check for
overflow. */
if (ckd_mul (&size, ttc->num_fonts, sizeof *ttc->offset_table))
{
xfree (ttc);
return NULL;
}
ttc = xrealloc (ttc, sizeof *ttc + size);
ttc->offset_table = (uint32_t *) (ttc + 1);
rc = read (fd, ttc->offset_table, size);
if (rc == -1 || rc < size)
{
xfree (ttc);
return NULL;
}
/* Swap each of the offsets read. */
for (i = 0; i < ttc->num_fonts; ++i)
sfnt_swap32 (&ttc->offset_table[i]);
/* Now, look at the version. If it is earlier than 2.0, then
reading is finished. */
if (ttc->version < 0x00020000)
return ttc;
/* If it is 2.0 or later, then continue to read ul_dsig_tag to
ul_dsig_offset. */
size = (SFNT_ENDOF (struct sfnt_ttc_header, ul_dsig_offset,
uint32_t)
- offsetof (struct sfnt_ttc_header, ul_dsig_tag));
rc = read (fd, &ttc->ul_dsig_tag, size);
if (rc == -1 || rc < size)
{
xfree (ttc);
return NULL;
}
/* Swap what was read. */
sfnt_swap32 (&ttc->ul_dsig_tag);
sfnt_swap32 (&ttc->ul_dsig_length);
sfnt_swap32 (&ttc->ul_dsig_offset);
/* All done. */
return ttc;
}
/* TrueType hinting support.
If you do not read the code in this section in conjunction with
Apple's TrueType Reference Manual, you will get very confused!
TrueType fonts don't provide simple hinting meta data, unlike Type
2 or CFF fonts.
Instead, they come with a ``font program'', a bytecode program
which is executed upon loading the font, a ``control value
program'', executed upon font metrics changing, and then a ``glyph
program'' for each glyph, which is run to fit its glyph after
scaling.
The virtual machine which runs this bytecode is arranged as such:
Firstly, there is a set of registers known as the ``graphics
state''. Each time the point size of a font changes, the ``control
value program'' is run to establish the default values of the
``graphics state''. Then, before each glyph program is run, the
``graphics state'' is set back to the default values.
Secondly, there is an address space which contains all instructions
being run for the current program, which is addressed by the
interpreter through its program counter and also by the
instructions which push data on to the stack.
Thirdly, there is a single stack, from which most instructions take
their operands and store data.
Then, there is some memory set aside for each font, the ``storage
area'', which is addressed through the RS[] and WS[] instructions,
and a ``control value table'', which is the `cvt ' table of the
font.
And finally, there is a ``glyph zone'' which holds points from a
scaled glyph outline, and a ``twilight zone'', which holds points
used by the font program itself. Both are addressed indirectly
through one of three ``zone pointer'' registers, and are accessible
only when a program is being run on behalf of a glyph. */
/* Functions for reading tables used by the TrueType interpreter. */
/* Read the cvt table (control value table) from the given font FD,
using the table directory specified as SUBTABLE. Swap all values
in the control value table. Return NULL upon failure, else the cvt
table. */
TEST_STATIC struct sfnt_cvt_table *
sfnt_read_cvt_table (int fd, struct sfnt_offset_subtable *subtable)
{
struct sfnt_table_directory *directory;
size_t required, i;
ssize_t rc;
struct sfnt_cvt_table *cvt;
/* Find the table in the directory. */
directory = sfnt_find_table (subtable, SFNT_TABLE_CVT );
if (!directory)
return NULL;
/* Seek to the location given in the directory. */
if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
return NULL;
/* Figure out the minimum amount that has to be read. */
if (ckd_add (&required, directory->length, sizeof *cvt))
return NULL;
/* Allocate enough for that much data. */
cvt = xmalloc (required);
/* Now set cvt->num_elements as appropriate, and make cvt->values
point into the values. */
cvt->num_elements = directory->length / 2;
cvt->values = (sfnt_fword *) (cvt + 1);
/* Read into cvt. */
rc = read (fd, cvt->values, directory->length);
if (rc != directory->length)
{
xfree (cvt);
return NULL;
}
/* Swap each element in the control value table. */
for (i = 0; i < cvt->num_elements; ++i)
sfnt_swap16 (&cvt->values[i]);
/* All done. */
return cvt;
}
/* Read the fpgm table from the given font FD, using the table
directory specified as SUBTABLE. Value is NULL upon failure, else
the fpgm table. */
TEST_STATIC struct sfnt_fpgm_table *
sfnt_read_fpgm_table (int fd, struct sfnt_offset_subtable *subtable)
{
struct sfnt_table_directory *directory;
size_t required;
ssize_t rc;
struct sfnt_fpgm_table *fpgm;
/* Find the table in the directory. */
directory = sfnt_find_table (subtable, SFNT_TABLE_FPGM);
if (!directory)
return NULL;
/* Seek to the location given in the directory. */
if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
return NULL;
/* Figure out the minimum amount that has to be read. */
if (ckd_add (&required, directory->length, sizeof *fpgm))
return NULL;
/* Allocate enough for that much data. */
fpgm = xmalloc (sizeof *fpgm + directory->length);
/* Now set fpgm->num_instructions as appropriate, and make
fpgm->instructions point to the right place. */
fpgm->num_instructions = directory->length;
fpgm->instructions = (unsigned char *) (fpgm + 1);
/* Read into fpgm. */
rc = read (fd, fpgm->instructions, directory->length);
if (rc != directory->length)
{
xfree (fpgm);
return NULL;
}
/* All done. */
return fpgm;
}
/* Read the prep table from the given font FD, using the table
directory specified as SUBTABLE. Value is NULL upon failure, else
the prep table. */
TEST_STATIC struct sfnt_prep_table *
sfnt_read_prep_table (int fd, struct sfnt_offset_subtable *subtable)
{
struct sfnt_table_directory *directory;
size_t required;
ssize_t rc;
struct sfnt_prep_table *prep;
/* Find the table in the directory. */
directory = sfnt_find_table (subtable, SFNT_TABLE_PREP);
if (!directory)
return NULL;
/* Seek to the location given in the directory. */
if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
return NULL;
/* Figure out the minimum amount that has to be read. */
if (ckd_add (&required, directory->length, sizeof *prep))
return NULL;
/* Allocate enough for that much data. */
prep = xmalloc (sizeof *prep + directory->length);
/* Now set prep->num_instructions as appropriate, and make
prep->instructions point to the right place. */
prep->num_instructions = directory->length;
prep->instructions = (unsigned char *) (prep + 1);
/* Read into prep. */
rc = read (fd, prep->instructions, directory->length);
if (rc != directory->length)
{
xfree (prep);
return NULL;
}
/* All done. */
return prep;
}
/* Interpreter execution environment. */
/* Divide the specified two 26.6 fixed point numbers X and Y.
Return the result. */
static sfnt_f26dot6
sfnt_div_f26dot6 (sfnt_f26dot6 x, sfnt_f26dot6 y)
{
#ifdef INT64_MAX
int64_t result;
result = ((int64_t) x * 64) / y;
return result;
#else
int sign;
unsigned int a, b;
sign = 1;
if (x < 0)
sign = -sign;
if (y < 0)
sign = -sign;
a = abs (x);
b = abs (y);
return sfnt_multiply_divide (a, 64, b) * sign;
#endif
}
/* Multiply the specified two 26.6 fixed point numbers A and B.
Return the result, or an undefined value upon overflow. */
static sfnt_f26dot6
sfnt_mul_f26dot6 (sfnt_f26dot6 a, sfnt_f26dot6 b)
{
#ifdef INT64_MAX
int64_t product;
product = (int64_t) a * (int64_t) b;
/* This can be done quickly with int64_t. */
return product / (int64_t) 64;
#else
int sign;
sign = 1;
if (a < 0)
sign = -sign;
if (b < 0)
sign = -sign;
return sfnt_multiply_divide (abs (a), abs (b),
64) * sign;
#endif
}
/* Multiply the specified two 26.6 fixed point numbers A and B, with
rounding. Return the result, or an undefined value upon
overflow. */
static sfnt_f26dot6
sfnt_mul_f26dot6_round (sfnt_f26dot6 a, sfnt_f26dot6 b)
{
#ifdef INT64_MAX
int64_t product;
product = (int64_t) a * (int64_t) b;
/* This can be done quickly with int64_t. */
return (product + 32) / (int64_t) 64;
#else /* !INT64_MAX */
int sign;
sign = 1;
if (a < 0)
sign = -sign;
if (b < 0)
sign = -sign;
return sfnt_multiply_divide_round (abs (a), abs (b),
32, 64) * sign;
#endif /* INT64_MAX */
}
/* Multiply the specified 2.14 number with another signed 32 bit
number. Return the result as a signed 32 bit number. */
static int32_t
sfnt_mul_f2dot14 (sfnt_f2dot14 a, int32_t b)
{
#ifdef INT64_MAX
int64_t product;
product = (int64_t) a * (int64_t) b;
return product / (int64_t) 16384;
#else
int sign;
sign = 1;
if (a < 0)
sign = -sign;
if (b < 0)
sign = -sign;
return sfnt_multiply_divide (abs (a), abs (b),
16384) * sign;
#endif
}
/* Multiply the specified 26.6 fixed point number X by the specified
16.16 fixed point number Y with rounding. */
static sfnt_f26dot6
sfnt_mul_f26dot6_fixed (sfnt_f26dot6 x, sfnt_fixed y)
{
return sfnt_mul_fixed_round (x, y);
}
/* Return the floor of the specified 26.6 fixed point value X. */
static sfnt_f26dot6
sfnt_floor_f26dot6 (sfnt_f26dot6 x)
{
return x & 037777777700;
}
/* Return the ceiling of the specified 26.6 fixed point value X. */
static sfnt_f26dot6
sfnt_ceil_f26dot6 (sfnt_f26dot6 x)
{
return (x + 077) & ~077;
}
/* Return the 26.6 fixed point value X rounded to the nearest integer
value. */
static sfnt_f26dot6
sfnt_round_f26dot6 (sfnt_f26dot6 x)
{
/* Add 0.5. */
x += 040;
/* Remove the fractional. */
return x & ~077;
}
/* Needed by sfnt_init_graphics_state. */
static void sfnt_validate_gs (struct sfnt_graphics_state *);
/* Set up default values for the interpreter graphics state. Return
them in STATE. */
static void
sfnt_init_graphics_state (struct sfnt_graphics_state *state)
{
state->auto_flip = true;
state->cvt_cut_in = 0104;
state->delta_base = 9;
state->delta_shift = 3;
state->dual_projection_vector.x = 040000; /* 1.0 */
state->dual_projection_vector.y = 0;
state->freedom_vector.x = 040000; /* 1.0 */
state->freedom_vector.y = 0;
state->instruct_control = 0;
state->loop = 1;
state->minimum_distance = 0100;
state->projection_vector.x = 040000; /* 1.0 */
state->projection_vector.y = 0;
state->round_state = 1;
state->rp0 = 0;
state->rp1 = 0;
state->rp2 = 0;
state->scan_control = 0;
state->sw_cut_in = 0;
state->single_width_value = 0;
state->zp0 = 1;
state->zp1 = 1;
state->zp2 = 1;
/* Validate the graphics state. */
sfnt_validate_gs (state);
}
/* Set up an interpreter to be used with a font. Use the resource
limits specified in the MAXP table, the values specified in the
CVT, HEAD and FVAR tables, the pixel size PIXEL_SIZE, and the point
size POINT_SIZE. CVT may be NULL, in which case the interpreter
will not have access to a control value table.
POINT_SIZE should be PIXEL_SIZE, converted to 1/72ths of an inch.
Value is the interpreter, with all state initialized to default
values, or NULL upon failure. */
TEST_STATIC struct sfnt_interpreter *
sfnt_make_interpreter (struct sfnt_maxp_table *maxp,
struct sfnt_cvt_table *cvt,
struct sfnt_head_table *head,
struct sfnt_fvar_table *fvar,
int pixel_size, int point_size)
{
size_t size, temp, i, storage_size, pad;
struct sfnt_interpreter *interpreter;
/* Detect CFF maxp tables. */
if (maxp->version != 0x00010000)
return NULL;
/* Use the contents of the MAXP table to determine the size of the
interpreter structure. */
size = sizeof (*interpreter);
/* Add program stack. */
if (ckd_add (&size, size, (maxp->max_stack_elements
* sizeof *interpreter->stack)))
return NULL;
/* Add twilight zone. */
if (ckd_add (&size, size, (maxp->max_twilight_points
* sizeof *interpreter->twilight_x)))
return NULL;
if (ckd_add (&size, size, (maxp->max_twilight_points
* sizeof *interpreter->twilight_y)))
return NULL;
if (ckd_add (&size, size, (maxp->max_twilight_points
* sizeof *interpreter->twilight_y)))
return NULL;
if (ckd_add (&size, size, (maxp->max_twilight_points
* sizeof *interpreter->twilight_y)))
return NULL;
/* Add the storage area. */
storage_size = maxp->max_storage * sizeof *interpreter->storage;
if (ckd_add (&size, size, storage_size))
return NULL;
/* Add padding for the storage area. */
pad = alignof (struct sfnt_interpreter_definition);
pad -= size & (pad - 1);
if (ckd_add (&size, size, pad))
return NULL;
/* Add function and instruction definitions. */
if (ckd_add (&size, size, (((int) maxp->max_instruction_defs
+ maxp->max_function_defs)
* sizeof *interpreter->function_defs)))
return NULL;
/* Add control value table. */
if (cvt)
{
if (ckd_mul (&temp, cvt->num_elements, sizeof *interpreter->cvt)
|| ckd_add (&size, size, temp))
return NULL;
}
/* Allocate the interpreter. */
interpreter = xmalloc (size);
#ifdef TEST
interpreter->run_hook = NULL;
interpreter->push_hook = NULL;
interpreter->pop_hook = NULL;
#endif /* TEST */
/* Fill in pointers and default values. */
interpreter->max_stack_elements = maxp->max_stack_elements;
interpreter->num_instructions = 0;
interpreter->IP = 0;
interpreter->storage_size = maxp->max_storage;
interpreter->function_defs_size = maxp->max_function_defs;
interpreter->instruction_defs_size = maxp->max_instruction_defs;
interpreter->twilight_zone_size = maxp->max_twilight_points;
interpreter->scale = 0; /* This should be set later. */
interpreter->stack = (uint32_t *) (interpreter + 1);
interpreter->SP = interpreter->stack;
interpreter->instructions = NULL;
interpreter->twilight_x
= (sfnt_f26dot6 *) (interpreter->stack
+ maxp->max_stack_elements);
interpreter->twilight_y = (interpreter->twilight_x
+ maxp->max_twilight_points);
interpreter->twilight_original_x = (interpreter->twilight_y
+ maxp->max_twilight_points);
interpreter->twilight_original_y
= (interpreter->twilight_original_x
+ maxp->max_twilight_points);
interpreter->glyph_zone = NULL;
interpreter->advance_width = 0;
interpreter->storage
= (uint32_t *) (interpreter->twilight_original_y
+ maxp->max_twilight_points);
interpreter->function_defs
= (struct sfnt_interpreter_definition *) (interpreter->storage
+ maxp->max_storage);
interpreter->function_defs
= ((struct sfnt_interpreter_definition *)
((unsigned char *) interpreter->function_defs + pad));
interpreter->instruction_defs = (interpreter->function_defs
+ maxp->max_function_defs);
interpreter->cvt
= (sfnt_f26dot6 *) (interpreter->instruction_defs
+ maxp->max_instruction_defs);
if (cvt)
interpreter->cvt_size = cvt->num_elements;
else
interpreter->cvt_size = 0;
/* Now compute the scale. Then, scale up the control value table
values. */
interpreter->scale
= sfnt_div_fixed (pixel_size * 64, head->units_per_em);
/* Set the PPEM. */
interpreter->ppem = pixel_size;
interpreter->point_size = point_size;
/* Zero out the interpreter state from the stack to the end of the
instruction definitions. */
memset (interpreter->stack, 0, size - sizeof *interpreter);
/* Initialize the interpreter graphics state. */
sfnt_init_graphics_state (&interpreter->state);
/* Load the control value table. */
for (i = 0; i < interpreter->cvt_size; ++i)
interpreter->cvt[i]
= sfnt_mul_f26dot6_fixed (cvt->values[i],
interpreter->scale);
/* Fill in the default values for phase, period and threshold. */
interpreter->period = 64;
interpreter->phase = 0;
interpreter->threshold = 0;
/* Fill in the current call depth. */
interpreter->call_depth = 0;
/* Clear variation axes. They will be set upon a call to
`sfnt_vary_interpreter'. */
interpreter->n_axis = 0;
interpreter->norm_coords = NULL;
/* Set n_axis now if a fvar table was provided. This way, GXAXIS
pushes the correct number of values even if no blend is
provided. */
if (fvar)
interpreter->n_axis = fvar->axis_count;
/* Return the interpreter. */
return interpreter;
}
/* These enums are used to determine why the interpreter is being
run. They have the following meanings:
- The interpreter is being run to interpret a font program.
- The interpreter is being run to interpret the control
value program.
- The interpreter is being run to fit a glyph.
- The interpreter is being run as part of an automated test. */
enum sfnt_interpreter_run_context
{
SFNT_RUN_CONTEXT_FONT_PROGRAM,
SFNT_RUN_CONTEXT_CONTROL_VALUE_PROGRAM,
SFNT_RUN_CONTEXT_GLYPH_PROGRAM,
#ifdef TEST
SFNT_RUN_CONTEXT_TEST,
#endif
};
/* Cancel execution of the program in INTERPRETER with the specified
error REASON and reset the loop counter to 1.
After this is called, it is probably okay to reuse INTERPRETER.
However, instructions must always be reloaded. */
static AVOID
sfnt_interpret_trap (struct sfnt_interpreter *interpreter,
const char *reason)
{
interpreter->trap_reason = reason;
interpreter->call_depth = 0;
interpreter->state.loop = 1;
longjmp (interpreter->trap, 1);
}
#define STACKSIZE() \
(interpreter->SP - interpreter->stack)
#define TRAP(why) \
sfnt_interpret_trap (interpreter, why)
#define MOVE(a, b, n) \
memmove (a, b, (n) * sizeof (uint32_t))
#define CHECK_STACK_ELEMENTS(n) \
{ \
if ((interpreter->SP \
- interpreter->stack) < n) \
TRAP ("stack underflow"); \
}
#define CHECK_STACK_AVAILABLE(n) \
{ \
char *stack_end; \
\
stack_end \
= (char *) interpreter->twilight_x; \
if (((char *) (interpreter->SP + (n)) \
> stack_end)) \
TRAP ("stack overflow"); \
}
#define CHECK_PREP() \
if (!is_prep) \
TRAP ("instruction executed not valid" \
" outside control value program") \
/* Register, alu and logic instructions. */
#ifndef TEST
#define POP() \
(interpreter->SP == interpreter->stack \
? (TRAP ("stack underflow"), 0) \
: (*(--interpreter->SP)))
#define POP_UNCHECKED() (*(--interpreter->SP))
#else
#define POP() \
(interpreter->SP == interpreter->stack \
? (TRAP ("stack underflow"), 0) \
: ({uint32_t _value; \
_value = *(--interpreter->SP); \
if (interpreter->pop_hook) \
interpreter->pop_hook (interpreter, \
_value); \
_value;}))
#define POP_UNCHECKED() POP ()
#endif
#define LOOK() \
(interpreter->SP == interpreter->stack \
? (TRAP ("stack underflow"), 0) \
: *(interpreter->SP - 1))
#if !defined TEST
#define PUSH(value) \
{ \
if ((char *) (interpreter->SP + 1) \
> (char *) interpreter->twilight_x) \
TRAP ("stack overflow"); \
\
*interpreter->SP = (value); \
interpreter->SP++; \
}
#define PUSH_UNCHECKED(value) \
{ \
*interpreter->SP = (value); \
interpreter->SP++; \
}
#else /* TEST */
#define PUSH(value) \
{ \
if ((char *) (interpreter->SP + 1) \
> (char *) interpreter->twilight_x) \
TRAP ("stack overflow"); \
\
if (interpreter->push_hook) \
interpreter->push_hook (interpreter, \
value); \
\
*interpreter->SP = value; \
interpreter->SP++; \
}
#define PUSH_UNCHECKED(value) PUSH (value)
#endif /* TEST && 0 */
#define PUSH2_UNCHECKED(high, low) \
{ \
int16_t word; \
\
word = (((uint8_t) high) << 8 | low); \
PUSH_UNCHECKED (word); \
} \
#define SRP0() \
{ \
uint32_t p; \
\
p = POP (); \
interpreter->state.rp0 = p; \
}
#define SRP1() \
{ \
uint32_t p; \
\
p = POP (); \
interpreter->state.rp1 = p; \
}
#define SRP2() \
{ \
uint32_t p; \
\
p = POP (); \
interpreter->state.rp2 = p; \
}
#define SZP0() \
{ \
uint32_t zone; \
\
zone = POP (); \
\
if (zone > 1) \
TRAP ("invalid zone"); \
\
interpreter->state.zp0 = zone; \
}
#define SZP1() \
{ \
uint32_t zone; \
\
zone = POP (); \
\
if (zone > 1) \
TRAP ("invalid zone"); \
\
interpreter->state.zp1 = zone; \
}
#define SZP2() \
{ \
uint32_t zone; \
\
zone = POP (); \
\
if (zone > 1) \
TRAP ("invalid zone"); \
\
interpreter->state.zp2 = zone; \
}
#define SZPS() \
{ \
uint32_t zone; \
\
zone = POP (); \
\
if (zone > 1) \
TRAP ("invalid zone"); \
\
interpreter->state.zp0 = zone; \
interpreter->state.zp1 = zone; \
interpreter->state.zp2 = zone; \
}
#define SLOOP() \
{ \
int32_t loop; \
\
loop = POP (); \
\
if (loop < 0) \
TRAP ("loop set to invalid value"); \
\
/* N.B. loop might be greater than 65535, \
but no reasonable font should define \
such values. */ \
interpreter->state.loop \
= MIN (65535, loop); \
}
#define SMD() \
{ \
sfnt_f26dot6 md; \
\
md = POP (); \
\
interpreter->state.minimum_distance = md; \
}
#define ELSE() \
{ \
sfnt_interpret_else (interpreter); \
goto skip_step; \
}
#define JMPR() \
{ \
int32_t offset; \
\
offset = POP (); \
\
if (interpreter->IP + offset < 0 \
|| (interpreter->IP + offset \
> interpreter->num_instructions)) \
TRAP ("JMPR out of bounds"); \
\
interpreter->IP += offset; \
goto skip_step; \
}
#define SCVTCI() \
{ \
sfnt_f26dot6 cutin; \
\
cutin = POP (); \
\
interpreter->state.cvt_cut_in = cutin; \
}
#define SSWCI() \
{ \
sfnt_f26dot6 cutin; \
\
cutin = POP (); \
\
interpreter->state.sw_cut_in = cutin; \
}
#define SSW() \
{ \
int32_t single_width; \
\
single_width = POP (); \
\
interpreter->state.single_width_value \
= sfnt_mul_fixed (single_width, \
interpreter->scale); \
}
#define DUP() \
{ \
uint32_t value; \
\
value = LOOK (); \
PUSH (value); \
}
#define CLEAR() \
{ \
interpreter->SP = interpreter->stack; \
}
#define SWAP() \
{ \
uint32_t a, b; \
\
a = POP (); \
b = POP (); \
\
PUSH_UNCHECKED (a); \
PUSH_UNCHECKED (b); \
}
#define DEPTH() \
{ \
ptrdiff_t diff; \
\
diff = (interpreter->SP \
- interpreter->stack); \
PUSH (diff); \
}
#define CINDEX() \
{ \
int32_t index; \
\
index = POP (); \
\
if (index <= 0 || index > STACKSIZE ()) \
TRAP ("stack overflow"); \
\
PUSH_UNCHECKED (*(interpreter->SP \
- index)); \
}
#define MINDEX() \
{ \
int32_t index, what; \
\
index = POP (); \
\
if (index <= 0 || index > STACKSIZE ()) \
TRAP ("stack overflow"); \
\
what = *(interpreter->SP - index); \
MOVE (interpreter->SP - index, \
interpreter->SP - index + 1, \
index - 1); \
*(interpreter->SP - 1) = what; \
}
#define RAW() \
{ \
if (why != SFNT_RUN_CONTEXT_GLYPH_PROGRAM) \
TRAP ("Read Advance Width without loaded" \
" glyph"); \
PUSH (interpreter->advance_width); \
}
#define CALL() \
{ \
uint32_t id, i; \
struct sfnt_interpreter_definition *def; \
\
id = POP (); \
\
for (i = 0; \
i < interpreter->function_defs_size; \
++i) \
{ \
def = &interpreter->function_defs[i]; \
\
if (!def->instructions) \
TRAP ("invalid function"); \
\
if (def->opcode == id) \
{ \
sfnt_interpret_call (def, \
interpreter, \
why); \
goto next_instruction; \
} \
} \
\
TRAP ("invalid function"); \
}
#define LOOPCALL() \
{ \
uint32_t id; \
int32_t n; \
int i; \
struct sfnt_interpreter_definition *def; \
\
id = POP (); \
n = POP (); \
\
if (n > 65535) \
TRAP ("invalid LOOPCALL count"); \
\
for (i = 0; \
i < interpreter->function_defs_size; \
++i) \
{ \
def = &interpreter->function_defs[i]; \
\
if (!def->instructions) \
TRAP ("invalid function"); \
\
if (def->opcode == id) \
goto loopcall_begin; \
} \
\
TRAP ("invalid function"); \
\
loopcall_begin: \
if (n-- <= 0) \
break; \
\
sfnt_interpret_call (def, interpreter, \
why); \
goto loopcall_begin; \
}
#define FDEF() \
{ \
if (why == SFNT_RUN_CONTEXT_GLYPH_PROGRAM) \
TRAP ("FDEF inside glyph program"); \
\
sfnt_interpret_fdef (interpreter, POP ()); \
goto skip_step; \
}
#define ENDF() \
{ \
TRAP ("stray ENDF"); \
}
#define NPUSHB() \
{ \
int b, nbytes, IP; \
unsigned char *ip; \
\
if ((IP = interpreter->IP + 1) \
>= interpreter->num_instructions) \
TRAP ("Missing arg to NPUSHB"); \
\
nbytes \
= interpreter->instructions[IP]; \
\
if (IP + 1 + nbytes \
> interpreter->num_instructions) \
TRAP ("args to NPUSHB lie outside IS"); \
\
CHECK_STACK_AVAILABLE (nbytes); \
ip = interpreter->instructions; \
for (b = IP + 1; b < IP + 1 + nbytes; ++b) \
PUSH_UNCHECKED (ip[b]); \
\
interpreter->IP += nbytes + 1; \
}
#define NPUSHW() \
{ \
int b, nbytes, IP; \
unsigned char *ip; \
\
if ((IP = interpreter->IP + 1) \
>= interpreter->num_instructions) \
TRAP ("Missing arg to NPUSHW"); \
\
nbytes \
= interpreter->instructions[IP] * 2; \
\
if (IP + 1 + nbytes \
> interpreter->num_instructions) \
TRAP ("args to NPUSHW lie outside IS"); \
\
CHECK_STACK_AVAILABLE (nbytes / 2); \
ip = interpreter->instructions; \
for (b = IP + 1; b < IP + 1 + nbytes; \
b += 2) \
PUSH2_UNCHECKED (ip[b], ip[b + 1]); \
\
interpreter->IP += nbytes + 1; \
}
#define WS() \
{ \
uint32_t address, value; \
\
value = POP (); \
address = POP (); \
\
if (address >= interpreter->storage_size) \
TRAP ("invalid WS"); \
\
interpreter->storage[address] = value; \
}
#define RS() \
{ \
uint32_t address, value; \
\
address = POP (); \
\
if (address >= interpreter->storage_size) \
TRAP ("invalid RS"); \
\
value = interpreter->storage[address]; \
PUSH_UNCHECKED (value); \
}
#define WCVTP() \
{ \
sfnt_f26dot6 value; \
uint32_t location; \
\
value = POP (); \
location = POP (); \
\
if (location >= interpreter->cvt_size) \
TRAP ("WCVTP out of bounds"); \
\
interpreter->cvt[location] = value; \
}
#define RCVT() \
{ \
sfnt_f26dot6 value; \
uint32_t location; \
\
location = POP (); \
\
if (location >= interpreter->cvt_size) \
TRAP ("out of bounds RCVT"); \
\
value = interpreter->cvt[location]; \
PUSH_UNCHECKED (value); \
}
#define MPPEM() \
{ \
PUSH (interpreter->ppem); \
}
#define MPS() \
{ \
PUSH (interpreter->point_size); \
}
#define FLIPON() \
{ \
interpreter->state.auto_flip = true; \
}
#define FLIPOFF() \
{ \
interpreter->state.auto_flip = false; \
}
#define DEBUG() \
{ \
POP (); /* Value is ignored. */ \
}
#define LT() \
{ \
int32_t e1, e2; \
\
e2 = POP (); \
e1 = POP (); \
\
PUSH_UNCHECKED (e1 < e2 ? 1 : 0); \
}
#define LTEQ() \
{ \
int32_t e1, e2; \
\
e2 = POP (); \
e1 = POP (); \
\
PUSH_UNCHECKED (e1 <= e2 ? 1 : 0); \
}
#define GT() \
{ \
int32_t e1, e2; \
\
e2 = POP (); \
e1 = POP (); \
\
PUSH_UNCHECKED (e1 > e2 ? 1 : 0); \
}
#define GTEQ() \
{ \
int32_t e1, e2; \
\
e2 = POP (); \
e1 = POP (); \
\
PUSH_UNCHECKED (e1 >= e2 ? 1 : 0); \
}
#define EQ() \
{ \
uint32_t e1, e2; \
\
e1 = POP (); \
e2 = POP (); \
\
PUSH_UNCHECKED (e1 == e2 ? 1 : 0); \
}
#define NEQ() \
{ \
uint32_t e1, e2; \
\
e1 = POP (); \
e2 = POP (); \
\
PUSH_UNCHECKED (e1 != e2 ? 1 : 0); \
}
#define ODD() \
{ \
sfnt_f26dot6 e1, result; \
\
e1 = POP (); \
result = abs (e1); \
\
result \
= interpreter->state.round (result, \
interpreter); \
PUSH_UNCHECKED (((result & 127) \
== 64) ? 1 : 0); \
}
#define EVEN() \
{ \
sfnt_f26dot6 e1, result; \
uint32_t value; \
\
e1 = POP (); \
result = abs (e1); \
\
result \
= interpreter->state.round (result, \
interpreter); \
value = ((result & 127) == 64) ? 0 : 1; \
PUSH_UNCHECKED (value); \
}
#define IF() \
{ \
uint32_t condition; \
\
condition = POP (); \
sfnt_interpret_if (interpreter, condition); \
goto skip_step; \
}
#define EIF() \
{ \
\
}
#define AND() \
{ \
uint32_t e1, e2; \
\
e1 = POP (); \
e2 = POP (); \
\
PUSH_UNCHECKED (e1 && e2 ? 1 : 0); \
}
#define OR() \
{ \
uint32_t e1, e2; \
\
e1 = POP (); \
e2 = POP (); \
\
PUSH_UNCHECKED (e1 || e2 ? 1 : 0); \
}
#define NOT() \
{ \
uint32_t e1; \
\
e1 = POP (); \
\
PUSH_UNCHECKED (!e1 ? 1 : 0); \
}
#define SDB() \
{ \
uint32_t base; \
\
base = POP (); \
\
interpreter->state.delta_base = base; \
}
#define SDS() \
{ \
uint32_t shift; \
\
shift = POP (); \
\
if (shift > 6) \
TRAP ("invalid delta shift"); \
\
interpreter->state.delta_shift = shift; \
}
#define ADD() \
{ \
sfnt_f26dot6 n1, n2; \
\
n1 = POP (); \
n2 = POP (); \
\
PUSH_UNCHECKED (sfnt_add (n1, n2)); \
}
#define SUB() \
{ \
sfnt_f26dot6 n2, n1; \
\
n2 = POP (); \
n1 = POP (); \
\
PUSH_UNCHECKED (sfnt_sub (n1, n2)); \
}
#define DIV() \
{ \
sfnt_f26dot6 n2, n1; \
\
n2 = POP (); \
n1 = POP (); \
\
if (!n2) \
TRAP ("DIV by 0"); \
\
PUSH_UNCHECKED (sfnt_div_f26dot6 (n1, n2)); \
}
#define MUL() \
{ \
sfnt_f26dot6 n2, n1, r; \
\
n2 = POP (); \
n1 = POP (); \
\
r = sfnt_mul_f26dot6_round (n2, n1); \
PUSH_UNCHECKED (r); \
}
#define ABS() \
{ \
sfnt_f26dot6 n; \
\
n = POP (); \
\
if (n == INT32_MIN) \
PUSH_UNCHECKED (0) \
else \
PUSH_UNCHECKED (n < 0 ? -n : n) \
}
#define NEG() \
{ \
sfnt_f26dot6 n; \
\
n = POP (); \
\
if (n == INT32_MIN) \
PUSH_UNCHECKED (0) \
else \
PUSH_UNCHECKED (-n) \
}
#define FLOOR() \
{ \
sfnt_f26dot6 n; \
\
n = POP (); \
PUSH_UNCHECKED (sfnt_floor_f26dot6 (n)); \
}
#define CEILING() \
{ \
sfnt_f26dot6 n; \
\
n = POP (); \
PUSH_UNCHECKED (sfnt_ceil_f26dot6 (n)); \
}
#define WCVTF() \
{ \
int32_t value; \
uint32_t location; \
\
value = POP (); \
location = POP (); \
\
if (location >= interpreter->cvt_size) \
TRAP ("WCVTF out of bounds"); \
\
interpreter->cvt[location] \
= sfnt_mul_fixed (value, \
interpreter->scale); \
}
#define JROT() \
{ \
uint32_t e; \
int32_t offset; \
\
e = POP (); \
offset = POP (); \
\
if (!e) \
break; \
\
if (interpreter->IP + offset < 0 \
|| (interpreter->IP + offset \
> interpreter->num_instructions)) \
TRAP ("JMPR out of bounds"); \
\
interpreter->IP += offset; \
goto skip_step; \
}
#define JROF() \
{ \
uint32_t e; \
int32_t offset; \
\
e = POP (); \
offset = POP (); \
\
if (e) \
break; \
\
if (interpreter->IP + offset < 0 \
|| (interpreter->IP + offset \
> interpreter->num_instructions)) \
TRAP ("JMPR out of bounds"); \
\
interpreter->IP += offset; \
goto skip_step; \
}
#define ILLEGAL_INSTRUCTION() \
{ \
TRAP ("MS reserved illegal instruction"); \
}
#define SCANCTRL() \
{ \
uint32_t value; \
\
value = POP (); \
interpreter->state.scan_control = value; \
}
/* Selector bit 3 is undocumented, but present in the Macintosh
rasterizer. 02000 is returned if there is a variation axis in
use.
Selector bit 5 is undocumented, but relied on by several fonts.
010000 is returned if a grayscale rasterizer is in use. */
#define GETINFO() \
{ \
uint32_t selector, k; \
\
selector = POP (); \
\
k = 0; \
\
if (selector & 1) \
k |= 02; \
\
if (selector & 8 \
&& interpreter->norm_coords) \
k |= 02000; \
\
if (selector & 32) \
k |= 010000; \
\
PUSH_UNCHECKED (k); \
}
#define IDEF() \
{ \
if (why == SFNT_RUN_CONTEXT_GLYPH_PROGRAM) \
TRAP ("IDEF inside glyph program"); \
\
sfnt_interpret_idef (interpreter, POP ()); \
goto skip_step; \
}
#define ROLL() \
{ \
uint32_t a, b, c; \
\
CHECK_STACK_ELEMENTS (3); \
\
a = POP_UNCHECKED (); \
b = POP_UNCHECKED (); \
c = POP_UNCHECKED (); \
\
PUSH_UNCHECKED (b); \
PUSH_UNCHECKED (a); \
PUSH_UNCHECKED (c); \
}
#define _MAX() \
{ \
int32_t e1, e2; \
\
e1 = POP (); \
e2 = POP (); \
\
PUSH_UNCHECKED (MAX (e1, e2)); \
}
#define _MIN() \
{ \
int32_t e1, e2; \
\
e1 = POP (); \
e2 = POP (); \
\
PUSH_UNCHECKED (MIN (e1, e2)); \
}
#define SCANTYPE() \
{ \
POP (); \
}
#define INSTCTRL() \
{ \
uint32_t s, v; \
\
CHECK_PREP (); \
s = POP (); \
v = POP (); \
\
if (!s || s > 2) \
break; \
\
interpreter->state.instruct_control \
&= ~(1 << s); \
\
if (v) \
interpreter->state.instruct_control \
|= (1 << s); \
}
/* GXAXIS is undocumented. It seems to return each axis in shortFrac
format. */
#define GXAXIS() \
{ \
uint32_t v; \
int i, naxis; \
\
naxis = interpreter->n_axis; \
CHECK_STACK_AVAILABLE (naxis); \
\
for (i = 0; i < naxis; ++i) \
{ \
if (interpreter->norm_coords) \
v = interpreter->norm_coords[i] / 4; \
else \
v = 0; \
\
PUSH_UNCHECKED (v); \
} \
}
#define PUSHB() \
{ \
int b, nbytes, IP; \
unsigned char *ip; \
\
IP = interpreter->IP; \
nbytes = opcode - 0xb0 + 1; \
\
if (IP + nbytes + 1 \
> interpreter->num_instructions) \
TRAP ("args to PUSHB lie outside IS"); \
\
CHECK_STACK_AVAILABLE (nbytes); \
ip = interpreter->instructions; \
for (b = IP + 1; b < IP + nbytes + 1; ++b) \
PUSH_UNCHECKED (ip[b]); \
\
interpreter->IP += nbytes; \
}
#define PUSHW() \
{ \
int b, nbytes, IP; \
unsigned char *ip; \
\
IP = interpreter->IP; \
nbytes = (opcode - 0xb8 + 1) * 2; \
\
if (IP + 1 + nbytes \
> interpreter->num_instructions) \
TRAP ("args to PUSHW lie outside IS"); \
\
CHECK_STACK_AVAILABLE (nbytes / 2); \
ip = interpreter->instructions; \
for (b = IP + 1; b < IP + nbytes + 1; \
b += 2) \
PUSH2_UNCHECKED (ip[b], ip[b + 1]); \
\
interpreter->IP += nbytes; \
}
/* Rounding instructions. */
#define ROUND() \
{ \
sfnt_f26dot6 n, result; \
\
n = POP (); \
result = abs (n); \
\
result \
= interpreter->state.round (result, \
interpreter); \
PUSH_UNCHECKED (n < 0 ? -result : result); \
}
#define NROUND() \
{ \
sfnt_f26dot6 n; \
\
n = POP (); \
PUSH_UNCHECKED (n); \
}
#define ROFF() \
{ \
interpreter->state.round_state = 5; \
sfnt_validate_gs (&interpreter->state); \
}
#define RUTG() \
{ \
interpreter->state.round_state = 4; \
sfnt_validate_gs (&interpreter->state); \
}
#define RDTG() \
{ \
interpreter->state.round_state = 3; \
sfnt_validate_gs (&interpreter->state); \
}
#define RTG() \
{ \
interpreter->state.round_state = 1; \
sfnt_validate_gs (&interpreter->state); \
}
#define RTHG() \
{ \
interpreter->state.round_state = 0; \
sfnt_validate_gs (&interpreter->state); \
}
#define RTDG() \
{ \
interpreter->state.round_state = 2; \
sfnt_validate_gs (&interpreter->state); \
}
#define SROUND() \
{ \
uint32_t operand; \
\
operand = POP (); \
sfnt_set_srounding_state (interpreter, \
operand, \
0x4000); \
interpreter->state.round_state = 6; \
sfnt_validate_gs (&interpreter->state); \
}
#define S45ROUND() \
{ \
uint32_t operand; \
\
operand = POP (); \
sfnt_set_srounding_state (interpreter, \
operand, \
0x5a82); \
interpreter->state.round_state = 7; \
sfnt_validate_gs (&interpreter->state); \
}
/* CVT and point delta exception instructions.
``Exceptions'' can be placed directly inside the control value
table, as it is reloaded every time the point size changes. */
#define DELTAC1() \
{ \
uint32_t operand1, operand2, n; \
\
n = POP (); \
\
deltac1_start: \
if (!n) \
break; \
\
operand1 = POP (); \
operand2 = POP (); \
sfnt_deltac (1, interpreter, operand1, \
operand2); \
n--; \
goto deltac1_start; \
}
#define DELTAC2() \
{ \
uint32_t operand1, operand2, n; \
\
n = POP (); \
\
deltac2_start: \
if (!n) \
break; \
\
operand1 = POP (); \
operand2 = POP (); \
sfnt_deltac (2, interpreter, operand1, \
operand2); \
n--; \
goto deltac2_start; \
}
#define DELTAC3() \
{ \
uint32_t operand1, operand2, n; \
\
n = POP (); \
\
deltac3_start: \
if (!n) \
break; \
\
operand1 = POP (); \
operand2 = POP (); \
sfnt_deltac (3, interpreter, operand1, \
operand2); \
n--; \
goto deltac3_start; \
}
#define DELTAP1() \
{ \
uint32_t n, argn, pn; \
\
n = POP (); \
\
deltap1_start: \
if (!n) \
break; \
\
pn = POP (); \
argn = POP (); \
sfnt_deltap (1, interpreter, argn, pn); \
n--; \
goto deltap1_start; \
}
#define DELTAP2() \
{ \
uint32_t n, argn, pn; \
\
n = POP (); \
\
deltap2_start: \
if (!n) \
break; \
\
pn = POP (); \
argn = POP (); \
sfnt_deltap (2, interpreter, argn, pn); \
n--; \
goto deltap2_start; \
}
#define DELTAP3() \
{ \
uint32_t n, argn, pn; \
\
n = POP (); \
\
deltap3_start: \
if (!n) \
break; \
\
pn = POP (); \
argn = POP (); \
sfnt_deltap (3, interpreter, argn, pn); \
n--; \
goto deltap3_start; \
}
/* Anachronistic angle instructions. */
#define AA() \
{ \
POP (); \
}
#define SANGW() \
{ \
POP (); \
}
/* Projection and freedom vector operations. */
#define PROJECT(x, y) \
sfnt_project_vector (interpreter, x, y)
#define DUAL_PROJECT(x, y) \
sfnt_dual_project_vector (interpreter, x, y)
#define SVTCAy() \
{ \
sfnt_set_freedom_vector (interpreter, \
0, 040000); \
sfnt_set_projection_vector (interpreter, \
0, 040000); \
}
#define SVTCAx() \
{ \
sfnt_set_freedom_vector (interpreter, \
040000, 0); \
sfnt_set_projection_vector (interpreter, \
040000, 0); \
}
#define SPvTCAy() \
{ \
sfnt_set_projection_vector (interpreter, \
0, 040000); \
}
#define SPvTCAx() \
{ \
sfnt_set_projection_vector (interpreter, \
040000, 0); \
}
#define SFvTCAy() \
{ \
sfnt_set_freedom_vector (interpreter, \
0, 040000); \
}
#define SFvTCAx() \
{ \
sfnt_set_freedom_vector (interpreter, \
040000, 0); \
}
#define SPVTL() \
{ \
struct sfnt_unit_vector vector; \
uint32_t p2, p1; \
\
p2 = POP (); \
p1 = POP (); \
\
sfnt_line_to_vector (interpreter, \
p2, p1, &vector, \
opcode == 0x07, \
false); \
\
sfnt_save_projection_vector (interpreter, \
&vector, \
false); \
}
#define SFVTL() \
{ \
struct sfnt_unit_vector vector; \
uint32_t p2, p1; \
\
p2 = POP (); \
p1 = POP (); \
\
sfnt_line_to_vector (interpreter, \
p2, p1, &vector, \
opcode == 0x09, \
false); \
\
sfnt_save_freedom_vector (interpreter, \
&vector); \
}
#define SPVFS() \
{ \
uint32_t y, x; \
\
y = POP (); \
x = POP (); \
\
sfnt_set_projection_vector (interpreter, x, \
y); \
}
#define SFVFS() \
{ \
uint16_t y, x; \
\
y = POP (); \
x = POP (); \
\
sfnt_set_freedom_vector (interpreter, x, \
y); \
}
#define GPV() \
{ \
struct sfnt_unit_vector vector; \
\
vector \
= interpreter->state.projection_vector; \
\
PUSH ((int32_t) vector.x); \
PUSH ((int32_t) vector.y); \
}
#define GFV() \
{ \
struct sfnt_unit_vector vector; \
\
vector \
= interpreter->state.freedom_vector; \
\
PUSH ((int32_t) vector.x); \
PUSH ((int32_t) vector.y); \
}
#define SFVTPV() \
{ \
interpreter->state.freedom_vector \
= interpreter->state.projection_vector; \
\
sfnt_validate_gs (&interpreter->state); \
}
#define ISECT() \
{ \
uint32_t a0, a1, b0, b1, p; \
\
CHECK_STACK_ELEMENTS (5); \
\
a0 = POP_UNCHECKED (); \
a1 = POP_UNCHECKED (); \
b0 = POP_UNCHECKED (); \
b1 = POP_UNCHECKED (); \
p = POP_UNCHECKED (); \
\
sfnt_interpret_isect (interpreter, \
a0, a1, b0, b1, p); \
}
#define ALIGNPTS() \
{ \
uint32_t p1, p2; \
\
p1 = POP (); \
p2 = POP (); \
\
sfnt_interpret_alignpts (interpreter, p1, \
p2); \
}
#define UTP() \
{ \
uint32_t p; \
\
p = POP (); \
sfnt_interpret_utp (interpreter, p); \
}
#define MDAP() \
{ \
uint32_t p; \
\
p = POP (); \
sfnt_interpret_mdap (interpreter, p, \
opcode); \
}
#define IUP() \
{ \
sfnt_interpret_iup (interpreter, opcode); \
}
#define SHP() \
{ \
sfnt_interpret_shp (interpreter, opcode); \
}
#define SHC() \
{ \
uint32_t contour; \
\
contour = POP (); \
\
sfnt_interpret_shc (interpreter, contour, \
opcode); \
}
#define SHZ() \
{ \
uint32_t e; \
\
e = POP (); \
\
if (e > 1) \
TRAP ("invalid zone!"); \
\
sfnt_interpret_shz (interpreter, e, \
opcode); \
}
#define SHPIX() \
{ \
sfnt_f26dot6 pixels, dx, dy; \
uint32_t p; \
\
pixels = POP (); \
sfnt_scale_by_freedom_vector (interpreter, \
pixels, &dx, \
&dy); \
\
while (interpreter->state.loop--) \
{ \
p = POP (); \
sfnt_direct_move_zp2 (interpreter, \
p, dx, dy); \
} \
\
interpreter->state.loop = 1; \
}
#define IP() \
{ \
sfnt_interpret_ip (interpreter); \
}
#define MSIRP() \
{ \
sfnt_f26dot6 d; \
uint32_t p; \
\
d = POP (); \
p = POP (); \
\
sfnt_interpret_msirp (interpreter, d, p, \
opcode); \
}
#define ALIGNRP() \
{ \
sfnt_interpret_alignrp (interpreter); \
}
#define MIAP() \
{ \
uint32_t cvt; \
uint32_t p; \
\
cvt = POP (); \
p = POP (); \
\
sfnt_interpret_miap (interpreter, cvt, p, \
opcode); \
}
#define GC() \
{ \
uint32_t p; \
sfnt_f26dot6 x, y, value; \
sfnt_f26dot6 org_x, org_y; \
\
p = POP (); \
\
sfnt_address_zp2 (interpreter, p, &x, &y, \
&org_x, &org_y); \
\
if (opcode == 0x47) \
value = DUAL_PROJECT (org_x, org_y); \
else \
value = PROJECT (x, y); \
\
PUSH_UNCHECKED (value); \
}
#define SCFS() \
{ \
uint32_t p; \
sfnt_f26dot6 c; \
\
c = POP (); \
p = POP (); \
\
sfnt_interpret_scfs (interpreter, p, c); \
}
#define MD() \
{ \
uint32_t p1, p2; \
sfnt_f26dot6 distance; \
\
p2 = POP (); \
p1 = POP (); \
\
distance \
= sfnt_measure_distance (interpreter, \
p1, p2, \
opcode); \
PUSH_UNCHECKED (distance); \
}
#define FLIPPT() \
{ \
sfnt_interpret_flippt (interpreter); \
}
#define FLIPRGOFF() \
{ \
uint32_t h, l; \
\
h = POP (); \
l = POP (); \
\
sfnt_interpret_fliprgoff (interpreter, \
h, l); \
}
#define FLIPRGON() \
{ \
uint32_t h, l; \
\
h = POP (); \
l = POP (); \
\
sfnt_interpret_fliprgon (interpreter, \
h, l); \
}
#define SDPVTL() \
{ \
struct sfnt_unit_vector vector; \
uint32_t p2, p1; \
\
p2 = POP (); \
p1 = POP (); \
\
sfnt_line_to_vector (interpreter, \
p2, p1, &vector, \
opcode == 0x87, \
false); \
\
sfnt_save_projection_vector (interpreter, \
&vector, \
false); \
\
sfnt_line_to_vector (interpreter, \
p2, p1, &vector, \
opcode == 0x87, \
true); \
\
sfnt_save_projection_vector (interpreter, \
&vector, \
true); \
}
#define MIRP() \
{ \
sfnt_interpret_mirp (interpreter, opcode); \
}
#define MDRP() \
{ \
sfnt_interpret_mdrp (interpreter, opcode); \
}
#define NOT_IMPLEMENTED() \
sfnt_interpret_unimplemented (interpreter, \
opcode, why)
/* Multiply the specified MAGNITUDE by the contents of INTERPRETER's
freedom vector and return the result in *DX and *DY. */
static void
sfnt_scale_by_freedom_vector (struct sfnt_interpreter *interpreter,
sfnt_f26dot6 magnitude, sfnt_f26dot6 *dx,
sfnt_f26dot6 *dy)
{
struct sfnt_unit_vector *vector;
vector = &interpreter->state.freedom_vector;
*dx = sfnt_mul_f2dot14 (vector->x, magnitude);
*dy = sfnt_mul_f2dot14 (vector->y, magnitude);
}
/* Interpret a UTP instruction with the point P in INTERPRETER.
Unset any ``touched'' flag inside the point P, relative to the
zone in INTERPRETER's ZP0 register.
Trap upon encountering an out of bounds point. */
static void
sfnt_interpret_utp (struct sfnt_interpreter *interpreter,
uint32_t p)
{
unsigned char mask;
if (!interpreter->state.zp0)
{
if (p >= interpreter->twilight_zone_size)
TRAP ("UTP[] p lies outside twilight zone");
/* There are no flags in the twilight zone. */
return;
}
if (!interpreter->glyph_zone
|| p >= interpreter->glyph_zone->num_points)
TRAP ("UTP[] p lies outside glyph zone");
/* The flags unset by UTP are subject to which axes in the freedom
vector are significant, as stated in the TrueType reference
manual by this needless mouthful:
A point may be touched in the x-direction, the y-direction, or
in both the x and y-directions. The position of the freedom
vector determines whether the point is untouched in the
x-direction, the y-direction, or both. If the vector is set to
the x-axis, the point will be untouched in the x-direction. If
the vector is set to the y-axis, the point will be untouched in
the y-direction. Otherwise the point will be untouched in both
directions.
A points that is marked as untouched will be moved by an IUP[]
instruction even if the point was previously touched. */
mask = 0xff;
if (interpreter->state.freedom_vector.x)
mask &= ~SFNT_POINT_TOUCHED_X;
if (interpreter->state.freedom_vector.y)
mask &= ~SFNT_POINT_TOUCHED_Y;
interpreter->glyph_zone->flags[p] &= mask;
}
/* Save the specified unit VECTOR into INTERPRETER's graphics state as
both the projection and the dual projection vectors.
If not DUAL_ONLY, set VECTOR as both the projection and dual
projection vectors. Otherwise, only set VECTOR as the dual
projection vector. */
static void
sfnt_save_projection_vector (struct sfnt_interpreter *interpreter,
struct sfnt_unit_vector *vector,
bool dual_only)
{
if (!dual_only)
interpreter->state.projection_vector = *vector;
interpreter->state.dual_projection_vector = *vector;
sfnt_validate_gs (&interpreter->state);
}
/* Save the specified unit VECTOR into INTERPRETER's graphics
state. */
static void
sfnt_save_freedom_vector (struct sfnt_interpreter *interpreter,
struct sfnt_unit_vector *vector)
{
interpreter->state.freedom_vector = *vector;
sfnt_validate_gs (&interpreter->state);
}
/* Return the values of the point NUMBER in the zone pointed to by
INTERPRETER's ZP2 register.
If X_ORG and Y_ORG are set, return the original values (prior to
any instruction interpretations) in those two locations.
Trap if NUMBER is out of bounds or the zone is inaccessible. */
static void
sfnt_address_zp2 (struct sfnt_interpreter *interpreter,
uint32_t number,
sfnt_f26dot6 *x, sfnt_f26dot6 *y,
sfnt_f26dot6 *x_org, sfnt_f26dot6 *y_org)
{
if (!interpreter->state.zp2)
{
/* Address the twilight zone. */
if (number >= interpreter->twilight_zone_size)
TRAP ("address to ZP2 (twilight zone) out of bounds");
if (!x || !y)
goto next;
*x = interpreter->twilight_x[number];
*y = interpreter->twilight_y[number];
next:
if (!x_org || !y_org)
return;
/* The twilight zone is initially all zero, but initial
positions can still be changed. */
*x_org = interpreter->twilight_original_x[number];
*y_org = interpreter->twilight_original_y[number];
return;
}
/* Address the glyph zone. */
if (!interpreter->glyph_zone)
TRAP ("address to ZP2 (glyph zone) points into unset"
" zone");
if (number >= interpreter->glyph_zone->num_points)
TRAP ("address to ZP2 (glyph zone) out of bounds");
if (x && y)
{
*x = interpreter->glyph_zone->x_current[number];
*y = interpreter->glyph_zone->y_current[number];
}
if (x_org && y_org)
{
*x_org = interpreter->glyph_zone->x_points[number];
*y_org = interpreter->glyph_zone->y_points[number];
}
}
/* Return the values of the point NUMBER in the zone pointed to by
INTERPRETER's ZP1 register.
Trap if NUMBER is out of bounds or the zone is inaccessible. */
static void
sfnt_address_zp1 (struct sfnt_interpreter *interpreter,
uint32_t number,
sfnt_f26dot6 *x, sfnt_f26dot6 *y,
sfnt_f26dot6 *x_org, sfnt_f26dot6 *y_org)
{
if (!interpreter->state.zp1)
{
/* Address the twilight zone. */
if (number >= interpreter->twilight_zone_size)
TRAP ("address to ZP1 (twilight zone) out of bounds");
if (!x || !y)
goto next;
*x = interpreter->twilight_x[number];
*y = interpreter->twilight_y[number];
next:
if (!x_org || !y_org)
return;
/* The twilight zone is initially all zero, but initial
positions can still be changed. */
*x_org = interpreter->twilight_original_x[number];
*y_org = interpreter->twilight_original_y[number];
return;
}
/* Address the glyph zone. */
if (!interpreter->glyph_zone)
TRAP ("address to ZP1 (glyph zone) points into unset"
" zone");
if (number >= interpreter->glyph_zone->num_points)
TRAP ("address to ZP1 (glyph zone) out of bounds");
if (x && y)
{
*x = interpreter->glyph_zone->x_current[number];
*y = interpreter->glyph_zone->y_current[number];
}
if (x_org && y_org)
{
*x_org = interpreter->glyph_zone->x_points[number];
*y_org = interpreter->glyph_zone->y_points[number];
}
}
/* Return the values of the point NUMBER in the zone pointed to by
INTERPRETER's ZP0 register.
Trap if NUMBER is out of bounds or the zone is inaccessible. */
static void
sfnt_address_zp0 (struct sfnt_interpreter *interpreter,
uint32_t number,
sfnt_f26dot6 *x, sfnt_f26dot6 *y,
sfnt_f26dot6 *x_org, sfnt_f26dot6 *y_org)
{
if (!interpreter->state.zp0)
{
/* Address the twilight zone. */
if (number >= interpreter->twilight_zone_size)
TRAP ("address to ZP0 (twilight zone) out of bounds");
if (!x || !y)
goto next;
*x = interpreter->twilight_x[number];
*y = interpreter->twilight_y[number];
next:
if (!x_org || !y_org)
return;
/* The twilight zone is initially all zero, but initial
positions can still be changed. */
*x_org = interpreter->twilight_original_x[number];
*y_org = interpreter->twilight_original_y[number];
return;
}
/* Address the glyph zone. */
if (!interpreter->glyph_zone)
TRAP ("address to ZP0 (glyph zone) points into unset"
" zone");
if (number >= interpreter->glyph_zone->num_points)
TRAP ("address to ZP0 (glyph zone) out of bounds");
if (x && y)
{
*x = interpreter->glyph_zone->x_current[number];
*y = interpreter->glyph_zone->y_current[number];
}
if (x_org && y_org)
{
*x_org = interpreter->glyph_zone->x_points[number];
*y_org = interpreter->glyph_zone->y_points[number];
}
}
/* Set the point NUMBER in the zone referenced by INTERPRETER's ZP2
register to the specified X and Y.
Apply FLAGS to NUMBER's flags in that zone. Trap if NUMBER is out
of bounds. */
static void
sfnt_store_zp2 (struct sfnt_interpreter *interpreter,
uint32_t number, sfnt_f26dot6 x, sfnt_f26dot6 y,
int flags)
{
if (!interpreter->state.zp2)
{
/* Address the twilight zone. */
if (number >= interpreter->twilight_zone_size)
TRAP ("address to ZP2 (twilight zone) out of bounds");
interpreter->twilight_x[number] = x;
interpreter->twilight_y[number] = y;
return;
}
/* Address the glyph zone. */
if (!interpreter->glyph_zone)
TRAP ("address to ZP0 (glyph zone) points into unset"
" zone");
if (number >= interpreter->glyph_zone->num_points)
TRAP ("address to ZP0 (glyph zone) out of bounds");
interpreter->glyph_zone->x_current[number] = x;
interpreter->glyph_zone->y_current[number] = y;
interpreter->glyph_zone->flags[number] |= flags;
}
#if 0
/* Convert the line between the points X1, Y1 and X2, Y2 to standard
form.
Return the two coefficients in *A0 and *B0, and the constant in
*C. */
static void
sfnt_line_to_standard_form (sfnt_f26dot6 x1, sfnt_f26dot6 y1,
sfnt_f26dot6 x2, sfnt_f26dot6 y2,
sfnt_f26dot6 *a, sfnt_f26dot6 *b,
sfnt_f26dot6 *c)
{
sfnt_f26dot6 a_temp, b_temp, c_temp;
a_temp = sfnt_sub (y2, y1);
b_temp = sfnt_sub (x1, x2);
c_temp = sfnt_sub (sfnt_mul_f26dot6 (x1, y2),
sfnt_mul_f26dot6 (x2, y1));
*a = a_temp;
*b = b_temp;
*c = c_temp;
}
#endif
/* Check that the specified POINT lies within the zone addressed by
INTERPRETER's ZP2 register. Trap if it does not. */
static void
sfnt_check_zp2 (struct sfnt_interpreter *interpreter, uint32_t point)
{
if (!interpreter->state.zp2)
{
if (point >= interpreter->twilight_zone_size)
TRAP ("point lies outside twilight zone (ZP2)");
}
else if (!interpreter->glyph_zone
|| point >= interpreter->glyph_zone->num_points)
TRAP ("point lies outside glyph zone (ZP2)");
}
/* Check that the specified POINT lies within the zone addressed by
INTERPRETER's ZP0 register. Trap if it does not. */
static void
sfnt_check_zp0 (struct sfnt_interpreter *interpreter, uint32_t point)
{
if (!interpreter->state.zp0)
{
if (point >= interpreter->twilight_zone_size)
TRAP ("point lies outside twilight zone (ZP0)");
}
else if (!interpreter->glyph_zone
|| point >= interpreter->glyph_zone->num_points)
TRAP ("point lies outside glyph zone (ZP0)");
}
/* Check that the specified POINT lies within the zone addressed by
INTERPRETER's ZP1 register. Trap if it does not. */
static void
sfnt_check_zp1 (struct sfnt_interpreter *interpreter, uint32_t point)
{
if (!interpreter->state.zp1)
{
if (point >= interpreter->twilight_zone_size)
TRAP ("point lies outside twilight zone (ZP0)");
}
else if (!interpreter->glyph_zone
|| point >= interpreter->glyph_zone->num_points)
TRAP ("point lies outside glyph zone (ZP0)");
}
/* Move N points starting from the specified POINT in the zone
addressed by INTERPRETER's ZP0 register by the given DISTANCE along
the freedom vector.
No checking is done to ensure that POINT lies inside the zone, or
even that the zone exists at all. */
static void
sfnt_move_zp0 (struct sfnt_interpreter *interpreter, uint32_t point,
size_t n, sfnt_f26dot6 distance)
{
if (!interpreter->state.zp0)
interpreter->state.move (&interpreter->twilight_x[point],
&interpreter->twilight_y[point],
n, interpreter, distance, NULL);
else
interpreter->state.move (&interpreter->glyph_zone->x_current[point],
&interpreter->glyph_zone->y_current[point],
n, interpreter, distance,
&interpreter->glyph_zone->flags[point]);
}
/* Move N points starting from the specified POINT in the zone
addressed by INTERPRETER's ZP1 register by the given DISTANCE along
the freedom vector.
No checking is done to ensure that POINT lies inside the zone, or
even that the zone exists at all. */
static void
sfnt_move_zp1 (struct sfnt_interpreter *interpreter, uint32_t point,
size_t n, sfnt_f26dot6 distance)
{
if (!interpreter->state.zp1)
interpreter->state.move (&interpreter->twilight_x[point],
&interpreter->twilight_y[point],
n, interpreter, distance, NULL);
else
interpreter->state.move (&interpreter->glyph_zone->x_current[point],
&interpreter->glyph_zone->y_current[point],
n, interpreter, distance,
&interpreter->glyph_zone->flags[point]);
}
/* Move N points starting from the specified POINT in the zone
addressed by INTERPRETER's ZP2 register by the given DISTANCE along
the freedom vector.
No checking is done to ensure that POINT lies inside the zone, or
even that the zone exists at all. */
static void
sfnt_move_zp2 (struct sfnt_interpreter *interpreter, uint32_t point,
size_t n, sfnt_f26dot6 distance)
{
if (!interpreter->state.zp2)
interpreter->state.move (&interpreter->twilight_x[point],
&interpreter->twilight_y[point],
n, interpreter, distance, NULL);
else
interpreter->state.move (&interpreter->glyph_zone->x_current[point],
&interpreter->glyph_zone->y_current[point],
n, interpreter, distance,
&interpreter->glyph_zone->flags[point]);
}
/* Move N points from the specified POINT in INTERPRETER's glyph zone
by the given DISTANCE along the freedom vector.
Do not touch the points that are moved.
No checking is done to ensure that POINT lies inside the zone, or
even that the zone exists at all. */
static void
sfnt_move_glyph_zone (struct sfnt_interpreter *interpreter, uint32_t point,
size_t n, sfnt_f26dot6 distance)
{
interpreter->state.move (&interpreter->glyph_zone->x_current[point],
&interpreter->glyph_zone->y_current[point],
n, interpreter, distance, NULL);
}
/* Move N points from the specified POINT in INTERPRETER's twilight
zone by the given DISTANCE along the freedom vector.
Do not touch the points that are moved.
No checking is done to ensure that POINT lies inside the zone, or
even that the zone exists at all. */
static void
sfnt_move_twilight_zone (struct sfnt_interpreter *interpreter, uint32_t point,
size_t n, sfnt_f26dot6 distance)
{
interpreter->state.move (&interpreter->twilight_x[point],
&interpreter->twilight_y[point],
n, interpreter, distance, NULL);
}
/* Move the point P in the zone pointed to by the ZP2 register in
INTERPRETER's graphics state by DX, and DY.
Touch the point P in the directions of the movement.
Check that P is valid; if not, trap. Else, perform the move
directly without converting it from the projection vector or to the
freedom vector. */
static void
sfnt_direct_move_zp2 (struct sfnt_interpreter *interpreter, uint32_t p,
sfnt_f26dot6 dx, sfnt_f26dot6 dy)
{
if (!interpreter->state.zp2)
{
if (p >= interpreter->twilight_zone_size)
TRAP ("point out of bounds");
interpreter->twilight_x[p]
= sfnt_add (interpreter->twilight_x[p], dx);
interpreter->twilight_y[p]
= sfnt_add (interpreter->twilight_y[p], dy);
}
else
{
if (!interpreter->glyph_zone
|| p >= interpreter->glyph_zone->num_points)
TRAP ("point out of bounds");
interpreter->glyph_zone->x_current[p]
= sfnt_add (interpreter->glyph_zone->x_current[p], dx);
interpreter->glyph_zone->y_current[p]
= sfnt_add (interpreter->glyph_zone->y_current[p], dy);
if (dx)
interpreter->glyph_zone->flags[p] |= SFNT_POINT_TOUCHED_X;
if (dy)
interpreter->glyph_zone->flags[p] |= SFNT_POINT_TOUCHED_Y;
}
}
/* Project the vector VX, VY onto INTERPRETER's projection vector.
Return the magnitude of the projection. */
static sfnt_f26dot6
sfnt_project_vector (struct sfnt_interpreter *interpreter,
sfnt_f26dot6 vx, sfnt_f26dot6 vy)
{
return interpreter->state.project (vx, vy, interpreter);
}
/* Project the vector VX, VY onto INTERPRETER's dual projection
vector. Return the magnitude of the projection. */
static sfnt_f26dot6
sfnt_dual_project_vector (struct sfnt_interpreter *interpreter,
sfnt_f26dot6 vx, sfnt_f26dot6 vy)
{
return interpreter->state.dual_project (vx, vy, interpreter);
}
/* Interpret a FLIPRGOFF instruction in INTERPRTER. Make each point
in ZP0 between L and H an off-curve point. */
static void
sfnt_interpret_fliprgoff (struct sfnt_interpreter *interpreter,
uint32_t h, uint32_t l)
{
uint32_t i;
sfnt_check_zp0 (interpreter, l);
sfnt_check_zp0 (interpreter, h);
if (!interpreter->state.zp0)
return;
for (i = l; i <= h; ++i)
interpreter->glyph_zone->flags[i] &= ~01;
}
/* Interpret a FLIPRGON instruction in INTERPRTER. Make each point in
ZP0 between L and H an on-curve point. */
static void
sfnt_interpret_fliprgon (struct sfnt_interpreter *interpreter,
uint32_t h, uint32_t l)
{
uint32_t i;
sfnt_check_zp0 (interpreter, l);
sfnt_check_zp0 (interpreter, h);
if (!interpreter->state.zp0)
return;
for (i = l; i <= h; ++i)
interpreter->glyph_zone->flags[i] |= 01;
}
/* Interpret a FLIPPT instruction in INTERPRETER. For loop times, pop
a point in ZP0. If it is an on-curve point, make it an off-curve
one, and vice versa. */
static void
sfnt_interpret_flippt (struct sfnt_interpreter *interpreter)
{
uint32_t point;
while (interpreter->state.loop--)
{
point = POP ();
/* There are no flags in the twilight zone.
But first check that the point is within bounds. */
sfnt_check_zp0 (interpreter, point);
if (!interpreter->state.zp0)
continue;
/* If POINT is on the curve, make it off the curve and vice
versa. */
if (interpreter->glyph_zone->flags[point] & 01)
interpreter->glyph_zone->flags[point] &= ~01;
else
interpreter->glyph_zone->flags[point] |= 01;
}
/* Restore loop. */
interpreter->state.loop = 1;
}
/* Interpret an SCFS instruction.
Move P in ZP2 along the freedom vector until its projection is
equal to C.
If ZP2 is the twilight zone, ``create'' P by setting its original
position to the projection. */
static void
sfnt_interpret_scfs (struct sfnt_interpreter *interpreter,
uint32_t p, sfnt_f26dot6 c)
{
sfnt_f26dot6 x, y, distance;
sfnt_address_zp2 (interpreter, p, &x, &y, NULL, NULL);
distance = PROJECT (x, y);
sfnt_move_zp2 (interpreter, p, 1, sfnt_sub (c, distance));
if (!interpreter->state.zp2)
{
interpreter->twilight_original_x[p] = interpreter->twilight_x[p];
interpreter->twilight_original_y[p] = interpreter->twilight_y[p];
}
}
/* Symmetrically round the 26.6 fixed point value X using the rounding
mode in INTERPRETER. Return the result. */
static sfnt_f26dot6
sfnt_round_symmetric (struct sfnt_interpreter *interpreter, sfnt_f26dot6 x)
{
int sign;
sign = 1;
if (x < 0)
{
sign = -1;
x = -x;
}
return interpreter->state.round (x, interpreter) * sign;
}
/* Interpret an MIAP (``Move Indirect Absolute Point'') instruction
using INTERPRETER.
Move P in ZP0 along the freedom vector until its projection on the
projection vector is equal to CVT units in the projection vector.
Finally, set RP0 and RP1 to P.
If ZP0 is the twilight zone, then first create that point in the
twilight zone by setting its ``original position'' to the
projection of the value.
If OPCODE is 0x3f, then in addition check the CVT value against the
control value cut-in, and round the magnitudes of the movement. */
static void
sfnt_interpret_miap (struct sfnt_interpreter *interpreter,
uint32_t cvt, uint32_t p, unsigned char opcode)
{
sfnt_f26dot6 x, y, distance, value, delta;
/* Read the cvt value. */
if (cvt >= interpreter->cvt_size)
TRAP ("out of bounds read to cvt");
value = interpreter->cvt[cvt];
/* Now load the point. */
sfnt_address_zp0 (interpreter, p, &x, &y, NULL, NULL);
/* Create the twilight zone point if necessary.
Note that the value used is not rounded. */
if (!interpreter->state.zp0)
{
x = interpreter->twilight_x[p]
= interpreter->twilight_original_x[p]
= sfnt_mul_f2dot14 (interpreter->state.projection_vector.x,
value);
y = interpreter->twilight_y[p]
= interpreter->twilight_original_y[p]
= sfnt_mul_f2dot14 (interpreter->state.projection_vector.y,
value);
}
/* Obtain the original distance. */
distance = sfnt_project_vector (interpreter, x, y);
/* Round the distance and apply the cvt cut in if necessary. */
if (opcode == 0x3f)
{
delta = sfnt_sub (value, distance);
if (delta < 0)
delta = -delta;
/* If delta is more than the cvt cut in (more aptly named ``cut
out''), use the original distance. */
if (delta > interpreter->state.cvt_cut_in)
value = distance;
/* Round value. */
value = sfnt_round_symmetric (interpreter, value);
}
/* Move the point by the distance. */
sfnt_move_zp0 (interpreter, p, 1, sfnt_sub (value, distance));
/* Set reference points. */
interpreter->state.rp0 = p;
interpreter->state.rp1 = p;
}
/* Perform a single iteration of sfnt_interpret_alignrp. RP0X and
RP0Y should be the position of the reference point RP0 in ZP0. */
static void
sfnt_interpret_alignrp_1 (struct sfnt_interpreter *interpreter,
sfnt_f26dot6 rp0x, sfnt_f26dot6 rp0y)
{
sfnt_f26dot6 distance, x, y;
uint32_t point;
point = POP ();
/* Load this point. */
sfnt_address_zp1 (interpreter, point, &x, &y, NULL, NULL);
/* Measure the distance from here to rp0. */
distance = sfnt_project_vector (interpreter, sfnt_sub (x, rp0x),
sfnt_sub (y, rp0y));
/* Move by the opposite. */
sfnt_move_zp1 (interpreter, point, 1, -distance);
}
/* For loop times, pop a point in ZP1 and align it to RP0 in ZP0 by
moving it along the freedom vector until its projected distance
from RP0 becomes 0. */
static void
sfnt_interpret_alignrp (struct sfnt_interpreter *interpreter)
{
sfnt_f26dot6 rp0x, rp0y;
sfnt_address_zp0 (interpreter, interpreter->state.rp0,
&rp0x, &rp0y, NULL, NULL);
while (interpreter->state.loop--)
{
sfnt_interpret_alignrp_1 (interpreter, rp0x, rp0y);
/* Reload RP0 if it is in the same zone as ZP1. */
if (interpreter->state.zp0 == interpreter->state.zp1)
sfnt_address_zp0 (interpreter, interpreter->state.rp0,
&rp0x, &rp0y, NULL, NULL);
}
interpreter->state.loop = 1;
}
/* Align the two points P1 and P2 relative to the projection vector.
P1 is addressed relative to ZP0, and P2 is addressed relative to
ZP1.
Move both points along the freedom vector by half the magnitude of
the the projection of a vector formed by P1.x - P2.x, P1.y - P2.y,
upon the projection vector. */
static void
sfnt_interpret_alignpts (struct sfnt_interpreter *interpreter,
uint32_t p1, uint32_t p2)
{
sfnt_f26dot6 p1x, p1y, p2x, p2y;
sfnt_f26dot6 magnitude;
sfnt_address_zp0 (interpreter, p1, &p1x, &p1y, NULL, NULL);
sfnt_address_zp1 (interpreter, p2, &p2x, &p2y, NULL, NULL);
magnitude = sfnt_project_vector (interpreter,
sfnt_sub (p1x, p2x),
sfnt_sub (p1y, p2y));
magnitude = magnitude / 2;
/* Now move both points along the freedom vector. */
sfnt_move_zp0 (interpreter, p1, 1, magnitude);
sfnt_move_zp1 (interpreter, p2, 1, -magnitude);
}
/* Set the point P in the zone referenced in INTERPRETER's ZP2
register to the intersection between the line formed by the points
POINT_A0 to POINT_A1 in ZP0 and another line formed by POINT_B0 to
POINT_B1 in ZP1.
Touch the point P. */
static void
sfnt_interpret_isect (struct sfnt_interpreter *interpreter,
uint32_t point_a0, uint32_t point_a1,
uint32_t point_b0, uint32_t point_b1,
uint32_t p)
{
sfnt_f26dot6 a0x, a0y, a1x, a1y;
sfnt_f26dot6 b0x, b0y, b1x, b1y;
#if 0
sfnt_f26dot6 determinant, dx, dy;
sfnt_f26dot6 a0, b0, a1, b1;
sfnt_f26dot6 c0, c1, px, py;
#else
sfnt_f26dot6 dx, dy, dax, day, dbx, dby;
sfnt_f26dot6 discriminant, val, dot_product;
sfnt_f26dot6 px, py;
#endif
/* Load points. */
sfnt_address_zp0 (interpreter, point_a0, &a0x, &a0y, NULL, NULL);
sfnt_address_zp0 (interpreter, point_a1, &a1x, &a1y, NULL, NULL);
sfnt_address_zp1 (interpreter, point_b0, &b0x, &b0y, NULL, NULL);
sfnt_address_zp1 (interpreter, point_b1, &b1x, &b1y, NULL, NULL);
#if 0
/* The system is determined from the standard form (look this up) of
both lines.
(the variables below have no relation to C identifiers
unless otherwise specified.)
a0*x + b0*y = c0
a1*x + b1*y = c1
The coefficient matrix is thus
[ a0 b0
a1 b1 ]
the vector of constants (also just dubbed the ``column vector''
by some people)
[ c0
c1 ]
and the solution vector becomes
[ x
y ]
Since there are exactly two equations and two unknowns, Cramer's
rule applies, and there is no need for any Gaussian elimination.
The determinant for the coefficient matrix is:
D = a0*b1 - b0*a1
the first and second determinants are:
Dx = c0*b1 - a0*c1
Dy = a1*c1 - c0*b1
and x = Dx / D, y = Dy / D.
If the system is indeterminate, D will be 0. */
sfnt_line_to_standard_form (a0x, a0y, a1x, a1y,
&a0, &b0, &c0);
sfnt_line_to_standard_form (b0x, b0y, b1x, b1y,
&a1, &b1, &c1);
/* Compute determinants. */
determinant = sfnt_sub (sfnt_mul_fixed (a0, b1),
sfnt_mul_fixed (b0, a1));
dx = sfnt_sub (sfnt_mul_fixed (c0, b1),
sfnt_mul_fixed (a1, c1));
dy = sfnt_sub (sfnt_mul_fixed (a0, c1),
sfnt_mul_fixed (c0, b0));
/* Detect degenerate cases. */
if (determinant == 0)
goto degenerate_case;
#else
/* The algorithm above would work with floating point, but overflows
too easily with fixed point numbers.
Instead, use the modified vector projection algorithm found in
FreeType. */
dbx = sfnt_sub (b1x, b0x);
dby = sfnt_sub (b1y, b0y);
dax = sfnt_sub (a1x, a0x);
day = sfnt_sub (a1y, a0y);
/* Compute vector cross product. */
discriminant = sfnt_add (sfnt_mul_f26dot6 (dax, -dby),
sfnt_mul_f26dot6 (day, dbx));
dot_product = sfnt_add (sfnt_mul_f26dot6 (dax, dbx),
sfnt_mul_f26dot6 (day, dby));
/* Reject any non-intersections and grazing intersections. */
if (!(sfnt_mul (19, abs (discriminant)) > abs (dot_product)))
return;
/* Reject any non-intersections. */
if (!discriminant)
goto degenerate_case;
dx = sfnt_sub (b0x, a0x);
dy = sfnt_sub (b0y, a0y);
val = sfnt_add (sfnt_mul_f26dot6 (dx, -dby),
sfnt_mul_f26dot6 (dy, dbx));
/* Project according to these values. */
dx = sfnt_add (a0x, sfnt_multiply_divide_signed (val, dax,
discriminant));
dy = sfnt_add (a0y, sfnt_multiply_divide_signed (val, day,
discriminant));
#endif
sfnt_store_zp2 (interpreter, p,
#if 0
sfnt_div_fixed (dx, determinant),
sfnt_div_fixed (dy, determinant),
#else
dx, dy,
#endif
SFNT_POINT_TOUCHED_BOTH);
return;
degenerate_case:
/* Apple says that in this case:
Px = (a0x + a1x) / 2 + (b0x + b1x) / 2
---------------------------------
2
Py = (a0y + a1y) / 2 + (b0y + b1y) / 2
---------------------------------
2 */
px = (sfnt_add (a0x, a1x) / 2 + sfnt_add (b0x, b1x) / 2) / 2;
py = (sfnt_add (a0y, a1y) / 2 + sfnt_add (b0y, b1y) / 2) / 2;
sfnt_store_zp2 (interpreter, p, px, py,
SFNT_POINT_TOUCHED_BOTH);
}
/* Compute the square root of the 16.16 fixed point number N. */
static sfnt_fixed
sfnt_sqrt_fixed (sfnt_fixed n)
{
int count;
unsigned int root, rem_hi, rem_lo, possible;
root = 0;
if (n > 0)
{
rem_hi = 0;
rem_lo = n;
count = 24;
do
{
rem_hi = (rem_hi << 2) | (rem_lo >> 30);
rem_lo <<= 2;
root <<= 1;
possible = (root << 1) + 1;
if (rem_hi >= possible)
{
rem_hi -= possible;
root += 1;
}
}
while (--count);
}
return root;
}
/* Compute a unit vector describing a vector VX, VY. Return the value
in *VECTOR. */
static void
sfnt_normalize_vector (sfnt_f26dot6 vx, sfnt_f26dot6 vy,
struct sfnt_unit_vector *vector)
{
sfnt_f26dot6 x_squared, y_squared;
sfnt_fixed n, magnitude;
if (!vx && !vy)
{
/* If vx and vy are both zero, then just project
horizontally. */
fail:
vector->x = 04000;
vector->y = 0;
return;
}
/* Scale vx and vy up if they won't at least make 1. */
while (!(vx < -32 || vx > 32) && !(vy < -32 || vy > 32))
{
vx = vx * 2;
vy = vy * 2;
}
/* Compute the magnitude of this vector. */
x_squared = sfnt_mul_f26dot6 (vx, vx);
y_squared = sfnt_mul_f26dot6 (vy, vy);
/* x_squared and y_squared can end up too large to fit in a 16.16
fixed. Scale both values down until they fit. */
while (x_squared > 0x200000 || y_squared > 0x200000
|| x_squared < -0x200000 || y_squared < -0x200000)
{
x_squared /= 2;
y_squared /= 2;
}
/* Convert to 16.16 for greater precision. */
n = sfnt_add (x_squared, y_squared) * 1024;
/* Get hypotenuse of the triangle from vx, 0, to 0, vy. */
magnitude = sfnt_sqrt_fixed (n);
/* Avoid division by zero. */
if (!magnitude)
goto fail;
/* Long division.. eek! */
vector->x = (sfnt_div_fixed (vx * 1024, magnitude) / 4);
vector->y = (sfnt_div_fixed (vy * 1024, magnitude) / 4);
}
/* Compute a unit vector describing the direction of a line from the
point P2 to the point P1. Save the result in *VECTOR.
P2 is the address of a point in the zone specified in the ZP2
register. P1 is the address of a point in the zone specified in
the ZP1 register. Take the values of both registers from the
specified INTERPRETER's graphics state.
If PERPENDICULAR, then *VECTOR will be rotated 90 degrees
counter-clockwise. Else, *VECTOR will be parallel to the line.
If ORIGINAL, then the coordinates used to calculate the line will
be those prior to instructing. Otherwise, the current coordinates
will be used. */
static void
sfnt_line_to_vector (struct sfnt_interpreter *interpreter,
uint32_t p2, uint32_t p1,
struct sfnt_unit_vector *vector,
bool perpendicular, bool original)
{
sfnt_f26dot6 x2, y2, original_x2, original_y2;
sfnt_f26dot6 x1, y1, original_x1, original_y1;
sfnt_f26dot6 a, b, temp;
sfnt_address_zp2 (interpreter, p2, &x2, &y2, &original_x2,
&original_y2);
sfnt_address_zp1 (interpreter, p1, &x1, &y1, &original_x1,
&original_y1);
/* Use original coordinates if specified. */
if (original)
{
x2 = original_x2;
y2 = original_y2;
x1 = original_x1;
y1 = original_y1;
}
/* Calculate the vector between X2, Y2, and X1, Y1. */
a = sfnt_sub (x1, x2);
b = sfnt_sub (y1, y2);
/* Rotate counterclockwise if necessary. */
if (perpendicular)
{
temp = b;
b = a;
a = -temp;
}
/* Normalize this vector, turning it into a unit vector. */
sfnt_normalize_vector (a, b, vector);
}
/* Measure the distance between P1 in ZP0 and P2 in ZP1,
relative to the projection or dual projection vector.
Return the distance of P1 and P2 relative to their original
un-instructed positions should OPCODE be 0x4A, and to their
instructed positions should OPCODE be 0x49. */
static sfnt_f26dot6
sfnt_measure_distance (struct sfnt_interpreter *interpreter,
uint32_t p1, uint32_t p2,
unsigned char opcode)
{
sfnt_f26dot6 p1x, p1y, p1_original_x, p1_original_y;
sfnt_f26dot6 p2x, p2y, p2_original_x, p2_original_y;
/* P1 is relative to ZP0 and P2 is relative to ZP1.
Apple's manual says this, Microsoft's does not. */
sfnt_address_zp0 (interpreter, p1, &p1x, &p1y,
&p1_original_x, &p1_original_y);
sfnt_address_zp1 (interpreter, p2, &p2x, &p2y,
&p2_original_x, &p2_original_y);
if (opcode == 0x4A)
return DUAL_PROJECT (sfnt_sub (p1_original_x, p2_original_x),
sfnt_sub (p1_original_y, p2_original_y));
return PROJECT (sfnt_sub (p1x, p2x),
sfnt_sub (p1y, p2y));
}
/* Interpret an MSIRP instruction in INTERPRETER.
Take a point P, and make the distance between P in ZP1 and the
current position of RP0 in ZP0 equal to D.
If ZP1 is the twilight zone, then create the point P by setting its
position and relative positions.
Then, if OPCODE is equal to 0x3b, make P RP0. */
static void
sfnt_interpret_msirp (struct sfnt_interpreter *interpreter,
sfnt_f26dot6 d, uint32_t p, unsigned char opcode)
{
sfnt_f26dot6 rp0x, rp0y, rp0_original_x, rp0_original_y;
sfnt_f26dot6 x, y;
sfnt_f26dot6 old_distance, temp;
sfnt_address_zp0 (interpreter, interpreter->state.rp0,
&rp0x, &rp0y, &rp0_original_x,
&rp0_original_y);
sfnt_address_zp1 (interpreter, p, &x, &y, NULL, NULL);
if (!interpreter->state.zp1)
{
/* Create this point in the twilight zone at RP0. */
x = interpreter->twilight_x[p] = rp0x;
y = interpreter->twilight_y[p] = rp0y;
/* Now set the original positions to the projected difference
from rp0. This makes sense once you think about it. */
temp = sfnt_mul_f2dot14 (interpreter->state.projection_vector.x, d);
temp = sfnt_add (temp, rp0_original_x);
interpreter->twilight_original_x[p] = temp;
temp = sfnt_mul_f2dot14 (interpreter->state.projection_vector.y, d);
temp = sfnt_add (temp, rp0_original_y);
interpreter->twilight_original_y[p] = temp;
}
/* Compute the original distance. */
old_distance = sfnt_project_vector (interpreter,
sfnt_sub (x, rp0x),
sfnt_sub (y, rp0y));
/* Move the point. */
sfnt_move_zp1 (interpreter, p, 1, sfnt_sub (d, old_distance));
/* Nothing in the TrueType reference manual says directly that this
instruction should change rp1 and rp2. However, it says this
instruction is ``very similar to the MIRP[] instruction
except...'', and FreeType seems to do this, so do it as well. */
interpreter->state.rp1 = interpreter->state.rp0;
interpreter->state.rp2 = p;
if (opcode == 0x3b)
interpreter->state.rp0 = p;
}
/* Interpret an IP instruction in INTERPRETER. For loop times, pop a
single point in ZP2, and interpolate it so that its original
relationship to the points RP1 in ZP0 and RP2 in ZP1 as measured
along the dual projection vector continues to hold true. */
static void
sfnt_interpret_ip (struct sfnt_interpreter *interpreter)
{
sfnt_f26dot6 rp1x, rp1y, rp1_original_x, rp1_original_y;
sfnt_f26dot6 rp2x, rp2y, rp2_original_x, rp2_original_y;
sfnt_f26dot6 range, new_range, org_distance, cur_distance;
sfnt_f26dot6 new_distance;
uint32_t p;
sfnt_f26dot6 x, y, original_x, original_y;
struct sfnt_interpreter_zone *zone;
bool scale;
/* First load both reference points. */
sfnt_address_zp0 (interpreter, interpreter->state.rp1,
&rp1x, &rp1y, &rp1_original_x,
&rp1_original_y);
sfnt_address_zp1 (interpreter, interpreter->state.rp2,
&rp2x, &rp2y, &rp2_original_x,
&rp2_original_y);
/* If RP1, RP2, and all arguments all fall within the glyph zone and
a simple glyph is loaded, replace their original coordinates as
loaded here with coordinates from the unscaled glyph outline. */
zone = interpreter->glyph_zone;
scale = false;
if (zone && zone->simple
&& interpreter->state.zp0
&& interpreter->state.zp1
&& interpreter->state.zp2)
{
p = interpreter->state.rp1;
/* If P is a phantom point... */
if (p >= zone->simple->number_of_points)
{
/* ...scale the phantom point to the size of the original
outline. */
rp1_original_x = sfnt_div_fixed (rp1_original_x,
interpreter->scale);
rp1_original_y = sfnt_div_fixed (rp1_original_y,
interpreter->scale);
}
else
{
rp1_original_x = zone->simple->x_coordinates[p];
rp1_original_y = zone->simple->y_coordinates[p];
}
p = interpreter->state.rp2;
/* If P is a phantom point... */
if (p >= zone->simple->number_of_points)
{
/* ...scale the phantom point to the size of the original
outline. */
rp2_original_x = sfnt_div_fixed (rp2_original_x,
interpreter->scale);
rp2_original_y = sfnt_div_fixed (rp2_original_y,
interpreter->scale);
}
else
{
rp2_original_x = zone->simple->x_coordinates[p];
rp2_original_y = zone->simple->y_coordinates[p];
}
scale = true;
}
/* Get the original distance between of RP1 and RP2 measured
relative to the dual projection vector. */
range = sfnt_dual_project_vector (interpreter,
sfnt_sub (rp2_original_x,
rp1_original_x),
sfnt_sub (rp2_original_y,
rp1_original_y));
if (scale)
range = sfnt_mul_fixed_round (range, interpreter->scale);
/* Get the new distance. */
new_range = sfnt_dual_project_vector (interpreter,
sfnt_sub (rp2x, rp1x),
sfnt_sub (rp2y, rp1y));
while (interpreter->state.loop--)
{
p = POP ();
/* Load this point relative to zp2. */
sfnt_address_zp2 (interpreter, p, &x, &y, &original_x,
&original_y);
if (scale)
{
/* If P is a phantom point... */
if (p >= zone->simple->number_of_points)
{
/* ...scale the phantom point to the size of the original
outline. */
original_x = sfnt_div_fixed (original_x,
interpreter->scale);
original_y = sfnt_div_fixed (original_y,
interpreter->scale);
}
else
{
original_x = zone->simple->x_coordinates[p];
original_y = zone->simple->y_coordinates[p];
}
}
/* Now compute the old distance from this point to rp1. */
org_distance
= sfnt_dual_project_vector (interpreter,
sfnt_sub (original_x,
rp1_original_x),
sfnt_sub (original_y,
rp1_original_y));
if (scale)
org_distance = sfnt_mul_fixed_round (org_distance,
interpreter->scale);
/* And the current distance from this point to rp1, so
how much to move can be determined. */
cur_distance
= sfnt_project_vector (interpreter,
sfnt_sub (x, rp1x),
sfnt_sub (y, rp1y));
/* Finally, apply the ratio of the new distance between RP1 and
RP2 to that of the old distance between the two reference
points to org_distance, making new_distance.
If both reference points occupy the same position on the dual
projection vector, then simply use the old distance. */
if (org_distance)
{
if (range)
new_distance
= sfnt_multiply_divide_signed (org_distance,
new_range, range);
else
new_distance = org_distance;
}
else
new_distance = 0;
/* And move the point along the freedom vector to reflect the
change in distance. */
sfnt_move_zp2 (interpreter, p, 1,
sfnt_sub (new_distance, cur_distance));
}
interpreter->state.loop = 1;
}
/* Apply the delta specified by OPERAND to the control value table
entry at INDEX currently loaded inside INTERPRETER.
Trap if INDEX is out of bounds.
NUMBER is the number of the specific DELTAC instruction this delta
is being applied on behalf of. It must be between 1 and 3. */
static void
sfnt_deltac (int number, struct sfnt_interpreter *interpreter,
unsigned int index, unsigned char operand)
{
int ppem, delta;
/* Make sure INDEX is a valid cvt entry. */
if (index >= interpreter->cvt_size)
TRAP ("DELTACn instruction out of bounds");
/* operand is an 8 bit number. The most significant 4 bits
represent a specific PPEM size at which to apply the delta
specified in the low 4 bits, summed with an instruction specific
delta, and the current delta base. */
ppem = (operand >> 4) + interpreter->state.delta_base;
switch (number)
{
case 1:
break;
case 2:
ppem += 16;
break;
case 3:
ppem += 32;
break;
}
/* Don't apply the delta if the ppem size doesn't match. */
if (interpreter->ppem != ppem)
return;
/* Now, determine the delta using the low 4 bits. The low 4 bits
actually specify a ``magnitude'' to apply to the delta, and do
not have an encoding for the delta 0. */
switch (operand & 0xf)
{
case 0:
delta = -8;
break;
case 1:
delta = -7;
break;
case 2:
delta = -6;
break;
case 3:
delta = -5;
break;
case 4:
delta = -4;
break;
case 5:
delta = -3;
break;
case 6:
delta = -2;
break;
case 7:
delta = -1;
break;
case 8:
delta = 1;
break;
case 9:
delta = 2;
break;
case 10:
delta = 3;
break;
case 11:
delta = 4;
break;
case 12:
delta = 5;
break;
case 13:
delta = 6;
break;
case 14:
delta = 7;
break;
case 15:
delta = 8;
break;
/* To pacify -fanalyzer. */
default:
abort ();
}
/* Now, scale up the delta by the step size, which is determined by
the delta shift. */
delta *= 1l << (6 - interpreter->state.delta_shift);
/* Finally, apply the delta to the CVT entry. */
interpreter->cvt[index] = sfnt_add (interpreter->cvt[index],
delta);
}
/* Interpret an MDAP (Move Direct Absolute Point) instruction with the
opcode OPCODE and the operand P in INTERPRETER.
Touch the point P (within the zone specified in zp0) in the
directions specified in the freedom vector. Then, if OPCODE is
0x2f, round the point and move it the rounded distance along the
freedom vector.
Finally, set the RP0 and RP1 registers to P. */
static void
sfnt_interpret_mdap (struct sfnt_interpreter *interpreter,
uint32_t p, uint32_t opcode)
{
sfnt_f26dot6 here, distance, px, py;
sfnt_address_zp0 (interpreter, p, &px, &py, NULL, NULL);
/* Measure the current distance. */
here = sfnt_project_vector (interpreter, px, py);
if (opcode == 0x2f)
{
/* Measure distance, round, then move to the distance. */
distance = sfnt_project_vector (interpreter, px, py);
distance = sfnt_round_symmetric (interpreter, distance);
distance = sfnt_sub (distance, here);
}
else
/* Don't move. Just touch the point. */
distance = 0;
sfnt_move_zp0 (interpreter, p, 1, distance);
interpreter->state.rp0 = p;
interpreter->state.rp1 = p;
}
/* Apply the delta specified by OPERAND to the point P in ZP0
currently loaded inside INTERPRETER.
Trap if P is out of bounds.
NUMBER is the number of the specific DELTAP instruction this delta
is being applied on behalf of. It must be between 1 and 3. */
static void
sfnt_deltap (int number, struct sfnt_interpreter *interpreter,
unsigned char operand, unsigned int p)
{
int ppem, delta;
/* Extract the ppem from OPERAND. The format is the same as in
sfnt_deltac. */
ppem = (operand >> 4) + interpreter->state.delta_base;
switch (number)
{
case 1:
break;
case 2:
ppem += 16;
break;
case 3:
ppem += 32;
break;
}
/* Don't apply the delta if the ppem size doesn't match. */
if (interpreter->ppem != ppem)
return;
/* Now, determine the magnitude of the movement and find the
delta. */
switch (operand & 0xf)
{
case 0:
delta = -8;
break;
case 1:
delta = -7;
break;
case 2:
delta = -6;
break;
case 3:
delta = -5;
break;
case 4:
delta = -4;
break;
case 5:
delta = -3;
break;
case 6:
delta = -2;
break;
case 7:
delta = -1;
break;
case 8:
delta = 1;
break;
case 9:
delta = 2;
break;
case 10:
delta = 3;
break;
case 11:
delta = 4;
break;
case 12:
delta = 5;
break;
case 13:
delta = 6;
break;
case 14:
delta = 7;
break;
case 15:
delta = 8;
break;
/* To pacify -fanalyzer. */
default:
abort ();
}
/* Now, scale up the delta by the step size, which is determined by
the delta shift. */
delta *= 1l << (6 - interpreter->state.delta_shift);
/* Move the point. */
sfnt_check_zp0 (interpreter, p);
sfnt_move_zp0 (interpreter, p, 1, delta);
}
/* Needed by sfnt_interpret_call. */
static void sfnt_interpret_run (struct sfnt_interpreter *,
enum sfnt_interpreter_run_context);
/* Call DEFINITION inside INTERPRETER.
Save INTERPRETER->IP, INTERPRETER->instructions, and
INTERPRETER->num_instructions onto the C stack.
Then, load the instructions in DEFINITION, and run the interpreter
again with the context CONTEXT.
Finally, restore all values. */
static void
sfnt_interpret_call (struct sfnt_interpreter_definition *definition,
struct sfnt_interpreter *interpreter,
enum sfnt_interpreter_run_context context)
{
uint16_t num_instructions;
int IP;
unsigned char *instructions;
/* Check that no recursion is going on. */
if (interpreter->call_depth++ >= 128)
TRAP ("CALL called CALL more than 127 times");
/* Save the old IP, instructions and number of instructions. */
num_instructions = interpreter->num_instructions;
IP = interpreter->IP;
instructions = interpreter->instructions;
/* Load and run the definition. */
interpreter->num_instructions = definition->instruction_count;
interpreter->instructions = definition->instructions;
interpreter->IP = 0;
sfnt_interpret_run (interpreter, context);
/* Restore the old values. */
interpreter->num_instructions = num_instructions;
interpreter->IP = IP;
interpreter->instructions = instructions;
interpreter->call_depth--;
}
/* Set the detailed rounding state in interpreter, on behalf of either
an SROUND or S45ROUND instruction that has been given the operand
OPERAND.
Use the specified GRID_PERIOD to determine the period. It is is a
18.14 fixed point number, but the rounding state set will be a 26.6
fixed point number. */
static void
sfnt_set_srounding_state (struct sfnt_interpreter *interpreter,
uint32_t operand, sfnt_f18dot14 grid_period)
{
sfnt_f18dot14 period, phase, threshold;
/* The most significant 2 bits in the 8 bit OPERAND determine the
period. */
switch ((operand & 0xc0) >> 6)
{
case 0:
period = grid_period / 2;
break;
case 1:
period = grid_period;
break;
case 2:
period = grid_period * 2;
break;
case 3:
default:
TRAP ("reserved period given to SROUND");
}
/* The next two bits determine the phase. */
switch ((operand & 0x30) >> 4)
{
case 0:
phase = 0;
break;
case 1:
phase = period / 4;
break;
case 2:
phase = period / 2;
break;
case 3:
default:
phase = period * 3 / 2;
break;
}
/* And the least significant 4 bits determine the threshold. */
if (operand & 0x0f)
threshold = (((int) (operand & 0x0f) - 4)
* period / 8);
else
threshold = period - 1;
/* Now extend these values to 26.6 format and set them. */
interpreter->period = period >> 8;
interpreter->phase = phase >> 8;
interpreter->threshold = threshold >> 8;
}
/* Move to the next opcode in INTERPRETER's instruction stream.
Value is the opcode originally at INTERPRETER->IP. */
static unsigned char
sfnt_skip_code (struct sfnt_interpreter *interpreter)
{
unsigned char opcode;
int nbytes;
if (interpreter->IP == interpreter->num_instructions)
TRAP ("IP at end of instruction stream");
/* Load opcode at IP. */
opcode = interpreter->instructions[interpreter->IP];
if (opcode == 0x40 || opcode == 0x41)
{
if (interpreter->IP + 1 >= interpreter->num_instructions)
TRAP ("Missing arg to NPUSHB or NPUSHW");
/* Figure out how many bytes or words to push. */
nbytes = interpreter->instructions[interpreter->IP + 1];
if (opcode == 0x41)
nbytes *= 2;
if (interpreter->IP + 2 + nbytes > interpreter->num_instructions)
TRAP ("args to NPUSH instruction lie outside IS");
/* Increment IP by so much. */
interpreter->IP += 2 + nbytes;
}
else if (opcode >= 0xb0 && opcode <= 0xb7)
{
nbytes = opcode - 0xb0 + 1;
if (interpreter->IP + 1 + nbytes > interpreter->num_instructions)
TRAP ("args to PUSHB instruction lie outide IS");
interpreter->IP += 1 + nbytes;
}
else if (opcode >= 0xb8 && opcode <= 0xbf)
{
nbytes = (opcode - 0xb8 + 1) * 2;
if (interpreter->IP + 1 + nbytes > interpreter->num_instructions)
TRAP ("args to PUSHW instruction lie outide IS");
interpreter->IP += 1 + nbytes;
}
else
interpreter->IP++;
return opcode;
}
/* Interpret the unimplemented operation OPCODE using INTERPRETER, and
the context WHY. If there is no instruction definition named
OPCODE, trap. */
static void
sfnt_interpret_unimplemented (struct sfnt_interpreter *interpreter,
unsigned char opcode,
enum sfnt_interpreter_run_context why)
{
uint32_t i;
struct sfnt_interpreter_definition *def;
for (i = 0; i < interpreter->instruction_defs_size; ++i)
{
def = &interpreter->instruction_defs[i];
if (def->opcode == opcode)
{
if (!def->instructions)
TRAP ("** ERROR ** malformed internal instruction"
" definition");
sfnt_interpret_call (def, interpreter, why);
return;
}
}
TRAP ("invalid instruction");
}
/* Start a function definition in INTERPRETER, with the function
opcode OPCODE. */
static void
sfnt_interpret_fdef (struct sfnt_interpreter *interpreter,
uint32_t opcode)
{
size_t i, num_fdefs;
int IP;
unsigned char instruction;
IP = interpreter->IP + 1;
num_fdefs = 0;
/* Now find an ENDF. */
while ((instruction = sfnt_skip_code (interpreter)) != 0x2d)
{
if (interpreter->IP >= interpreter->num_instructions)
TRAP ("missing ENDF");
/* If this is an FDEF or IDEF instruction, increment num_fdefs.
Prohibit nested FDEFs or IDEFS. */
if (instruction == 0x2c || instruction == 0x89)
++num_fdefs;
if (num_fdefs > 1)
TRAP ("IDEF or FDEF before ENDF");
}
/* ENDF has been found. Now save the function definition. Try to
find an existing function definition with this opcode. If that
fails, make i the first available function definition. */
for (i = 0; i < interpreter->function_defs_size; ++i)
{
if (interpreter->function_defs[i].opcode == opcode
|| !interpreter->function_defs[i].instructions)
break;
}
if (i == interpreter->function_defs_size)
TRAP ("number of fdefs exceeded maxp->max_function_defs");
/* Save the opcode of this function definition. */
interpreter->function_defs[i].opcode = opcode;
/* Make sure to ignore the trailing ENDF instruction. */
interpreter->function_defs[i].instruction_count
= interpreter->IP - IP - 1;
/* Now save a pointer to the instructions. */
interpreter->function_defs[i].instructions = interpreter->instructions + IP;
}
/* Start an instruction definition in INTERPRETER, with the
instruction opcode OPCODE. */
static void
sfnt_interpret_idef (struct sfnt_interpreter *interpreter,
uint32_t opcode)
{
size_t i, num_fdefs;
int IP;
unsigned char instruction;
IP = interpreter->IP + 1;
num_fdefs = 0;
/* Now find an ENDF. */
while ((instruction = sfnt_skip_code (interpreter)) != 0x2d)
{
if (interpreter->IP >= interpreter->num_instructions)
TRAP ("missing ENDF");
/* If this is an FDEF or IDEF instruction, increment num_fdefs.
Prohibit nested FDEFs or IDEFS. */
if (instruction == 0x2c || instruction == 0x89)
++num_fdefs;
if (num_fdefs > 1)
TRAP ("IDEF or FDEF before ENDF");
}
/* ENDF has been found. Now save the instruction definition. Try to
find an existing instruction definition with this opcode. If that
fails, make i the first available instruction definition. */
for (i = 0; i < interpreter->instruction_defs_size; ++i)
{
if (interpreter->instruction_defs[i].opcode == opcode
|| !interpreter->instruction_defs[i].instructions)
break;
}
if (i == interpreter->instruction_defs_size)
TRAP ("number of defs exceeded maxp->max_instruction_defs");
/* Save the opcode of this instruction definition. */
interpreter->instruction_defs[i].opcode = opcode;
/* Make sure to ignore the trailing ENDF instruction. */
interpreter->instruction_defs[i].instruction_count
= interpreter->IP - IP - 1;
/* Now save a pointer to the instructions. */
interpreter->instruction_defs[i].instructions
= interpreter->instructions + IP;
}
/* Interpret the specified conditional at INTERPRETER->IP.
If CONDITION, evaluate this branch up until the next ELSE or ENDIF.
Else, evaluate the branch from a matching ELSE condition, if
one exists. */
static void
sfnt_interpret_if (struct sfnt_interpreter *interpreter,
bool condition)
{
int nifs;
bool need_break;
unsigned char opcode;
if (condition)
{
interpreter->IP++;
return;
}
/* Number of ifs. */
nifs = 0;
need_break = false;
/* Break past the matching else condition. */
do
{
/* Load the current opcode, then increase IP. */
opcode = sfnt_skip_code (interpreter);
if (interpreter->IP >= interpreter->num_instructions)
break;
switch (opcode)
{
case 0x58: /* IF */
nifs++;
break;
case 0x1B: /* ELSE */
if (nifs == 1)
need_break = true;
break;
case 0x59: /* EIF */
nifs--;
if (nifs == 0)
need_break = true;
break;
}
}
while (!need_break);
}
/* Interpret the specified ELSE branch at INTERPRETER->IP.
Evaluate starting from a matching ENDIF instruction.
If IF has set INTERPRETER->IP to a code within an ELSE branch, this
will not be called. */
static void
sfnt_interpret_else (struct sfnt_interpreter *interpreter)
{
int nifs;
unsigned char opcode;
/* Number of ifs. */
nifs = 1;
/* Break past the matching ENDIF condition. */
do
{
/* Load the current opcode, then increase IP. */
opcode = sfnt_skip_code (interpreter);
if (interpreter->IP >= interpreter->num_instructions)
break;
switch (opcode)
{
case 0x58: /* IF */
nifs++;
break;
case 0x59: /* EIF */
nifs--;
break;
}
}
while (nifs > 0);
}
/* ``Add engine compensation to X''. Since engine compensation is not
implemented here, this simply returns X. INTERPRETER is
unused. */
static sfnt_f26dot6
sfnt_round_none (sfnt_f26dot6 x, struct sfnt_interpreter *interpreter)
{
return x;
}
/* Round X to the grid after adding engine compensation. Return the
result. INTERPRETER is unused. */
static sfnt_f26dot6
sfnt_round_to_grid (sfnt_f26dot6 x, struct sfnt_interpreter *interpreter)
{
return sfnt_round_f26dot6 (x);
}
/* Round X to the nearest half integer or integer and return the
result. INTERPRETER is unused. */
static sfnt_f26dot6
sfnt_round_to_double_grid (sfnt_f26dot6 x,
struct sfnt_interpreter *interpreter)
{
return (x + 020) & ~037;
}
/* Take the floor of X and return the result. INTERPRETER is
unused. */
static sfnt_f26dot6
sfnt_round_down_to_grid (sfnt_f26dot6 x,
struct sfnt_interpreter *interpreter)
{
return sfnt_floor_f26dot6 (x);
}
/* Take the ceiling of X and return the result. INTERPRETER is
unused. */
static sfnt_f26dot6
sfnt_round_up_to_grid (sfnt_f26dot6 x,
struct sfnt_interpreter *interpreter)
{
return sfnt_ceil_f26dot6 (x);
}
/* Round X to only the nearest half integer and return the result.
INTERPRETER is unused. */
static sfnt_f26dot6
sfnt_round_to_half_grid (sfnt_f26dot6 x,
struct sfnt_interpreter *interpreter)
{
return sfnt_floor_f26dot6 (x) + 32;
}
/* Round X using the detailed rounding information ``super rounding
state'' in INTERPRETER. Value is the result. */
static sfnt_f26dot6
sfnt_round_super (sfnt_f26dot6 x,
struct sfnt_interpreter *interpreter)
{
sfnt_f26dot6 value;
/* Compute the rounded value. */
value = sfnt_add ((interpreter->threshold
- interpreter->phase), x);
value = sfnt_add (value & -interpreter->period,
interpreter->phase);
/* Remember that since the phase is specified by font instructions,
it is possible for the sign to be changed. In that case, return
the phase itself. */
return value < 0 ? interpreter->phase : value;
}
/* Round X using the detailed rounding information ``super rounding
state'' in INTERPRETER, but suitably for values that are multiples
of the sqrt of 2. Value is the result. */
static sfnt_f26dot6
sfnt_round_super45 (sfnt_f26dot6 x,
struct sfnt_interpreter *interpreter)
{
sfnt_f26dot6 value;
/* Compute the rounded value. */
value = ((sfnt_add (x, (interpreter->threshold
- interpreter->phase))
/ interpreter->period)
* interpreter->period);
value = sfnt_add (value, interpreter->phase);
/* Remember that since the phase is specified by font instructions,
it is possible for the sign to be changed. In that case, return
the phase itself. */
return value < 0 ? interpreter->phase : value;
}
/* Project the specified vector VX and VY onto the unit vector that is
INTERPRETER's projection vector, assuming that INTERPRETER's
projection vector is on the X axis.
Value is the magnitude of the projected vector. */
static sfnt_f26dot6
sfnt_project_onto_x_axis_vector (sfnt_f26dot6 vx, sfnt_f26dot6 vy,
struct sfnt_interpreter *interpreter)
{
return vx;
}
/* Project the specified vector VX and VY onto the unit vector that is
INTERPRETER's projection vector, assuming that INTERPRETER's
projection vector is on the Y axis.
Value is the magnitude of the projected vector. */
static sfnt_f26dot6
sfnt_project_onto_y_axis_vector (sfnt_f26dot6 vx, sfnt_f26dot6 vy,
struct sfnt_interpreter *interpreter)
{
return vy;
}
/* Calculate AX * BX + AY * BY divided by 16384. */
static int32_t
sfnt_dot_fix_14 (int32_t ax, int32_t ay, int bx, int by)
{
#ifndef INT64_MAX
int32_t m, s, hi1, hi2, hi;
uint32_t l, lo1, lo2, lo;
/* Compute ax*bx as 64-bit value. */
l = (uint32_t) ((ax & 0xffffu) * bx);
m = (ax >> 16) * bx;
lo1 = l + ((uint32_t) m << 16);
hi1 = (m >> 16) + ((int32_t) l >> 31) + (lo1 < l);
/* Compute ay*by as 64-bit value. */
l = (uint32_t) ((ay & 0xffffu) * by);
m = (ay >> 16) * by;
lo2 = l + ((uint32_t) m << 16);
hi2 = (m >> 16) + ((int32_t) l >> 31) + (lo2 < l);
/* Add them. */
lo = lo1 + lo2;
hi = hi1 + hi2 + (lo < lo1);
/* Divide the result by 2^14 with rounding. */
s = hi >> 31;
l = lo + (uint32_t) s;
hi += s + (l < lo);
lo = l;
l = lo + 0x2000u;
hi += (l < lo);
return (int32_t) (((uint32_t) hi << 18) | (l >> 14));
#else
int64_t xx, yy;
int64_t temp;
xx = (int64_t) ax * bx;
yy = (int64_t) ay * by;
xx += yy;
yy = xx >> 63;
xx += 0x2000 + yy;
/* TrueType fonts rely on "division" here truncating towards
negative infinity, so compute the arithmetic right shift in place
of division. */
temp = -(xx < 0);
temp = (temp ^ xx) >> 14 ^ temp;
return (int32_t) (temp);
#endif
}
/* Project the specified vector VX and VY onto the unit vector that is
INTERPRETER's projection vector, making only the assumption that the
projection vector is a valid unit vector.
Value is the magnitude of the projected vector. */
static sfnt_f26dot6
sfnt_project_onto_any_vector (sfnt_f26dot6 vx, sfnt_f26dot6 vy,
struct sfnt_interpreter *interpreter)
{
return sfnt_dot_fix_14 (vx, vy,
interpreter->state.projection_vector.x,
interpreter->state.projection_vector.y);
}
/* Project the specified vector VX and VY onto the unit vector that is
INTERPRETER's dual projection vector, making only the assumption
that the dual projection vector is a valid unit vector.
The dual projection vector is a vector that is normally the
projection vector, but can be set using the original unscaled
coordinates of two points as well.
Value is the magnitude of the projected vector. */
static sfnt_f26dot6
sfnt_dual_project_onto_any_vector (sfnt_f26dot6 vx, sfnt_f26dot6 vy,
struct sfnt_interpreter *interpreter)
{
return sfnt_dot_fix_14 (vx, vy,
interpreter->state.dual_projection_vector.x,
interpreter->state.dual_projection_vector.y);
}
/* Move N points at *X, *Y by DISTANCE along INTERPRETER's freedom
vector. Set N flags in *FLAGS where appropriate and when non-NULL.
Assume both vectors are aligned to the X axis. */
static void
sfnt_move_x (sfnt_f26dot6 *restrict x, sfnt_f26dot6 *restrict y,
size_t n, struct sfnt_interpreter *interpreter,
sfnt_f26dot6 distance, unsigned char *flags)
{
while (n--)
{
*x = sfnt_add (*x, distance);
x++;
if (flags)
*flags++ |= SFNT_POINT_TOUCHED_X;
}
}
/* Move N points at *X, *Y by DISTANCE along INTERPRETER's freedom
vector. Set N flags in *FLAGS where appropriate and when non-NULL.
Assume both vectors are aligned to the Y axis. */
static void
sfnt_move_y (sfnt_f26dot6 *restrict x, sfnt_f26dot6 *restrict y,
size_t n, struct sfnt_interpreter *interpreter,
sfnt_f26dot6 distance, unsigned char *flags)
{
while (n--)
{
*y = sfnt_add (*y, distance);
y++;
if (flags)
*flags++ |= SFNT_POINT_TOUCHED_Y;
}
}
/* Move N points at *X, *Y by DISTANCE along INTERPRETER's freedom
vector. Set N flags in *FLAGS where appropriate and when
non-NULL. */
static void
sfnt_move (sfnt_f26dot6 *restrict x, sfnt_f26dot6 *restrict y,
size_t n, struct sfnt_interpreter *interpreter,
sfnt_f26dot6 distance, unsigned char *flags)
{
sfnt_f26dot6 versor, k;
sfnt_f2dot14 dot_product;
size_t num;
unsigned char *flags_start;
dot_product = interpreter->state.vector_dot_product;
/* If the vectors are orthogonal, it is impossible to move anywhere,
so simply return. */
if (!dot_product)
return;
/* Not actually 26.6, but the multiply-divisions below cancel each
other out, so the result is 26.6. */
versor = interpreter->state.freedom_vector.x;
/* Save flags that it may be restored for the second Y axis
loop. */
flags_start = flags;
if (versor)
{
/* Move along X axis, converting the distance to the freedom
vector. */
num = n;
k = sfnt_multiply_divide_signed (distance,
versor,
dot_product);
while (num--)
{
*x = sfnt_add (*x, k);
x++;
if (flags)
*flags++ |= SFNT_POINT_TOUCHED_X;
}
}
flags = flags_start;
versor = interpreter->state.freedom_vector.y;
if (versor)
{
/* Move along Y axis, converting the distance to the freedom
vector. */
num = n;
k = sfnt_multiply_divide_signed (distance,
versor,
dot_product);
while (num--)
{
*y = sfnt_add (*y, k);
y++;
if (flags)
*flags++ |= SFNT_POINT_TOUCHED_Y;
}
}
}
/* Compute the dot product of the two versors A and B with
rounding. */
static sfnt_f2dot14
sfnt_short_frac_dot (sfnt_f2dot14 a, sfnt_f2dot14 b)
{
return (sfnt_f2dot14) ((((long) a * b) + 8192) / 16384);
}
/* Validate the graphics state GS.
Establish function pointers for rounding and projection.
Establish dot product used to convert vector distances between
each other. */
static void
sfnt_validate_gs (struct sfnt_graphics_state *gs)
{
/* Establish the function used for rounding based on the round
state. */
switch (gs->round_state)
{
case 5: /* Rounding off. */
gs->round = sfnt_round_none;
break;
case 0: /* Round to half grid. */
gs->round = sfnt_round_to_half_grid;
break;
case 1: /* Round to grid. */
gs->round = sfnt_round_to_grid;
break;
case 2: /* Round to double grid. */
gs->round = sfnt_round_to_double_grid;
break;
case 4: /* Round up to grid. */
gs->round = sfnt_round_up_to_grid;
break;
case 3: /* Round down to grid. */
gs->round = sfnt_round_down_to_grid;
break;
case 6: /* Fine grained rounding. */
gs->round = sfnt_round_super;
break;
case 7: /* Fine grained rounding 45 degree variant. */
gs->round = sfnt_round_super45;
break;
}
/* Establish the function used for vector projection.
When the projection vector is an axis vector, a fast
version can be used. */
if (gs->projection_vector.x == 040000)
gs->project = sfnt_project_onto_x_axis_vector;
else if (gs->projection_vector.y == 040000)
gs->project = sfnt_project_onto_y_axis_vector;
else
gs->project = sfnt_project_onto_any_vector;
/* Do the same for the dual projection vector. */
if (gs->dual_projection_vector.x == 040000)
gs->dual_project = sfnt_project_onto_x_axis_vector;
else if (gs->dual_projection_vector.y == 040000)
gs->dual_project = sfnt_project_onto_y_axis_vector;
else
gs->dual_project = sfnt_dual_project_onto_any_vector;
/* Compute dot product of the freedom and projection vectors.
Handle the common case where the freedom vector is aligned
to an axis. */
if (gs->freedom_vector.x == 040000)
gs->vector_dot_product = gs->projection_vector.x;
else if (gs->freedom_vector.y == 040000)
gs->vector_dot_product = gs->projection_vector.y;
else
/* Actually calculate the dot product. */
gs->vector_dot_product = (sfnt_short_frac_dot (gs->projection_vector.x,
gs->freedom_vector.x)
+ sfnt_short_frac_dot (gs->projection_vector.y,
gs->freedom_vector.y));
/* If the product is less than 1/16th of a vector, prevent overflow
by resetting it to 1. */
if (gs->vector_dot_product > -0x400
&& gs->vector_dot_product < 0x400)
gs->vector_dot_product = (gs->vector_dot_product < 0
? -0x4000 : 0x4000);
/* Now figure out which function to use to move distances. Handle
the common case where both the freedom and projection vectors are
aligned to an axis. */
if (gs->freedom_vector.x == 040000
&& gs->projection_vector.x == 040000)
gs->move = sfnt_move_x;
else if (gs->freedom_vector.y == 040000
&& gs->projection_vector.y == 040000)
gs->move = sfnt_move_y;
else
gs->move = sfnt_move;
}
/* Set the X and Y versors of the freedom vector of INTERPRETER's
graphics state to the specified X and Y, in 2.14 fixed point
format. */
static void
sfnt_set_freedom_vector (struct sfnt_interpreter *interpreter,
sfnt_f2dot14 x, sfnt_f2dot14 y)
{
interpreter->state.freedom_vector.x = x;
interpreter->state.freedom_vector.y = y;
sfnt_validate_gs (&interpreter->state);
}
/* Set the X and Y versors of the projection vector of INTERPRETER's
graphics state to the specified X and Y, in 2.14 fixed point
format. */
static void
sfnt_set_projection_vector (struct sfnt_interpreter *interpreter,
sfnt_f2dot14 x, sfnt_f2dot14 y)
{
interpreter->state.projection_vector.x = x;
interpreter->state.projection_vector.y = y;
interpreter->state.dual_projection_vector.x = x;
interpreter->state.dual_projection_vector.y = y;
sfnt_validate_gs (&interpreter->state);
}
/* Interpret an SHZ instruction with the specified OPCODE. Like
sfnt_interpret_shc, but do the move for each point in the entire
specified ZONE. */
static void
sfnt_interpret_shz (struct sfnt_interpreter *interpreter,
uint32_t zone, unsigned int opcode)
{
sfnt_f26dot6 x, y, original_x, original_y;
sfnt_f26dot6 magnitude;
if (zone != 0 && !interpreter->glyph_zone)
/* There are no points in the glyph zone. */
return;
if (opcode == 0x37)
sfnt_address_zp0 (interpreter, interpreter->state.rp1,
&x, &y, &original_x, &original_y);
else
sfnt_address_zp1 (interpreter, interpreter->state.rp2,
&x, &y, &original_x, &original_y);
magnitude = sfnt_project_vector (interpreter,
sfnt_sub (x, original_x),
sfnt_sub (y, original_y));
if (zone == 0)
sfnt_move_twilight_zone (interpreter, 0,
interpreter->twilight_zone_size,
magnitude);
else
sfnt_move_glyph_zone (interpreter, 0,
interpreter->glyph_zone->num_points,
magnitude);
}
/* Interpret an SHC instruction with the specified OPCODE and CONTOUR.
Like sfnt_interpret_shp, but do the move for each point in the
specified contour. */
static void
sfnt_interpret_shc (struct sfnt_interpreter *interpreter,
uint32_t contour, unsigned int opcode)
{
sfnt_f26dot6 x, y, original_x, original_y;
sfnt_f26dot6 magnitude;
uint16_t reference_point;
size_t start, end, start1, end1, n;
if (!interpreter->glyph_zone)
TRAP ("SHC without glyph zone");
/* Check that the contour is within bounds. */
if (contour >= interpreter->glyph_zone->num_contours)
TRAP ("contour out of bounds");
/* Figure out the magnitude of the change, measured from the
projection vector. */
if (opcode == 0x35)
sfnt_address_zp0 (interpreter,
(reference_point = interpreter->state.rp1),
&x, &y, &original_x, &original_y);
else
sfnt_address_zp1 (interpreter,
(reference_point = interpreter->state.rp2),
&x, &y, &original_x, &original_y);
magnitude = sfnt_project_vector (interpreter,
sfnt_sub (x, original_x),
sfnt_sub (y, original_y));
/* Now obtain the start and end of the contour.
Verify that both are valid. */
if (contour)
start = interpreter->glyph_zone->contour_end_points[contour - 1] + 1;
else
start = 0;
end = interpreter->glyph_zone->contour_end_points[contour];
if (start > end || end >= interpreter->glyph_zone->num_points)
TRAP ("invalid contour data in glyph");
/* If the reference point falls between end and start, split the
range formed by end and start at the reference point and keep the
latter intact. */
if (start <= reference_point && reference_point <= end)
{
/* Do the points between start and rpN. */
start1 = start;
end1 = reference_point - 1;
if (start1 <= end1)
sfnt_move_glyph_zone (interpreter, start1,
end1 - start1 + 1, magnitude);
/* Now the points between rpN + 1 and end. */
start1 = reference_point + 1;
end1 = end;
if (start1 <= end1)
sfnt_move_glyph_zone (interpreter, start1,
end1 - start1 + 1, magnitude);
return;
}
/* Compute the number of points to move. */
n = end - start + 1;
/* Move that many points. */
sfnt_move_glyph_zone (interpreter, start, n, magnitude);
}
/* Interpret an SHP instruction with the specified OPCODE. Move a
popped point in ZP2 along the freedom vector by the distance
between a specified point from its original position, which is RP1
in ZP0 if OPCODE is 0x33, and RP2 in ZP1 if OPCODE is 0x32.
Repeat for the number of iterations specified by a prior SLOOP
instruction. */
static void
sfnt_interpret_shp (struct sfnt_interpreter *interpreter,
unsigned int opcode)
{
sfnt_f26dot6 x, y, original_x, original_y;
sfnt_f26dot6 magnitude;
uint32_t point;
/* Figure out the magnitude of the change, measured from the
projection vector. */
if (opcode == 0x33)
sfnt_address_zp0 (interpreter, interpreter->state.rp1,
&x, &y, &original_x, &original_y);
else
sfnt_address_zp1 (interpreter, interpreter->state.rp2,
&x, &y, &original_x, &original_y);
magnitude = sfnt_project_vector (interpreter,
sfnt_sub (x, original_x),
sfnt_sub (y, original_y));
/* Now project it onto the freedom vector and move the point that
much for loop variable times. */
while (interpreter->state.loop--)
{
point = POP ();
sfnt_check_zp2 (interpreter, point);
sfnt_move_zp2 (interpreter, point, 1, magnitude);
}
/* Restore interpreter->state.loop to 1. */
interpreter->state.loop = 1;
}
#define load_point(p) \
(opcode == 0x31 \
? interpreter->glyph_zone->x_current[p] \
: interpreter->glyph_zone->y_current[p])
#define store_point(p, val) \
(opcode == 0x31 \
? (interpreter->glyph_zone->x_current[p] = (val)) \
: (interpreter->glyph_zone->y_current[p] = (val)))
#define load_original(p) \
(opcode == 0x31 \
? interpreter->glyph_zone->x_points[p] \
: interpreter->glyph_zone->y_points[p])
#define load_unscaled(p) \
(opcode == 0x31 \
? interpreter->glyph_zone->simple->x_coordinates[p] \
: interpreter->glyph_zone->simple->y_coordinates[p])
#define IUP_SINGLE_PAIR() \
/* Now make touch_start the first point before, i.e. the first \
touched point in this pair. */ \
\
if (touch_start == start) \
touch_start = end; \
else \
touch_start = touch_start - 1; \
\
/* Set point_min and point_max based on which glyph is at a \
lower value. */ \
\
if (load_original (touch_start) < load_original (touch_end)) \
{ \
point_min = touch_start; \
point_max = touch_end; \
} \
else \
{ \
point_max = touch_start; \
point_min = touch_end; \
} \
\
min_pos = load_point (point_min); \
max_pos = load_point (point_max); \
\
/* This is needed for interpolation. */ \
original_max_pos = load_original (point_max); \
original_min_pos = load_original (point_min); \
\
/* Now process points between touch_start and touch_end. */ \
\
i = touch_start + 1; \
\
/* touch_start might be the last point in the contour. */ \
\
if (i > end) \
i = start; \
\
while (i != touch_end) \
{ \
/* Movement is always relative to the original position of \
the point. */ \
position = load_original (i); \
\
/* If i is in between touch_start and touch_end... */ \
if (position >= original_min_pos \
&& position <= original_max_pos) \
{ \
/* Compute the ratio between the two touched point positions \
and the original position of the point being touched with \
positions from the unscaled outline, if at all \
possible. */ \
\
if (interpreter->glyph_zone->simple) \
{ \
org_max_pos = load_unscaled (point_max); \
org_min_pos = load_unscaled (point_min); \
position = load_unscaled (i); \
} \
else \
{ \
org_max_pos = original_max_pos; \
org_min_pos = original_min_pos; \
} \
\
/* Handle the degenerate case where original_min_pos and \
original_max_pos have not changed by placing the point in \
the middle. */ \
if (org_min_pos == org_max_pos) \
ratio = 077777; \
else \
/* ... preserve the ratio of i between min_pos and \
max_pos... */ \
ratio = sfnt_div_fixed ((sfnt_sub (position, \
org_min_pos) \
* 1024), \
(sfnt_sub (org_max_pos, \
org_min_pos) \
* 1024)); \
\
delta = sfnt_sub (max_pos, min_pos); \
delta = sfnt_mul_fixed_round (ratio, delta); \
store_point (i, sfnt_add (min_pos, delta)); \
} \
else \
{ \
/* ... otherwise, move i by how much the nearest touched \
point moved. */ \
\
if (position >= original_max_pos) \
delta = sfnt_sub (max_pos, original_max_pos); \
else \
delta = sfnt_sub (min_pos, original_min_pos); \
\
store_point (i, sfnt_add (position, delta)); \
} \
\
if (++i > end) \
i = start; \
} \
/* Interpolate untouched points in the contour between and including
START and END inside INTERPRETER's glyph zone according to the
rules specified for an IUP instruction. Perform interpolation on
the axis specified by OPCODE and MASK. */
static void
sfnt_interpret_iup_1 (struct sfnt_interpreter *interpreter,
size_t start, size_t end,
unsigned char opcode, int mask)
{
size_t point;
size_t touch_start, touch_end;
size_t first_point;
size_t point_min, point_max, i;
sfnt_f26dot6 position, min_pos, max_pos, delta, ratio;
sfnt_f26dot6 original_max_pos, org_max_pos;
sfnt_f26dot6 original_min_pos, org_min_pos;
/* Find the first touched point. If none is found, simply
return. */
for (point = start; point <= end; ++point)
{
if (interpreter->glyph_zone->flags[point] & mask)
goto touched;
}
goto untouched;
touched:
point = start;
/* Find the first touched point. */
while (!(interpreter->glyph_zone->flags[point] & mask))
{
point++;
/* There are no touched points. */
if (point > end)
goto untouched;
}
first_point = point;
while (point <= end)
{
/* Find the next untouched point. */
while (interpreter->glyph_zone->flags[point] & mask)
{
point++;
if (point > end)
goto wraparound;
}
/* touch_start is now the first untouched point. */
touch_start = point;
/* Find the next touched point. */
while (!(interpreter->glyph_zone->flags[point] & mask))
{
point++;
/* Move back to start if point has gone past the end of the
contour. */
if (point > end)
goto wraparound_1;
}
/* touch_end is now the next touched point. */
touch_end = point;
/* Do the interpolation. */
IUP_SINGLE_PAIR ();
}
goto untouched;
wraparound:
/* This is like wraparound_1, except that no untouched points have
yet to be found.
This means the first untouched point is start. */
touch_start = start;
wraparound_1:
/* If point > end, wrap around. Here, touch_start is set
properly, so touch_end must be first_point. */
touch_end = first_point;
IUP_SINGLE_PAIR ();
untouched:
/* No points were touched or all points have been considered, so
return immediately. */
return;
}
#undef load_point
#undef store_point
#undef load_original
#undef load_unscaled
/* Interpret an IUP (``interpolate untouched points'') instruction.
INTERPRETER is the interpreter, and OPCODE is the instruction
number. See the TrueType Reference Manual for more details. */
static void
sfnt_interpret_iup (struct sfnt_interpreter *interpreter,
unsigned char opcode)
{
int mask;
size_t i, point, end, first_point;
/* Check that the zone is the glyph zone. */
if (!interpreter->state.zp2)
TRAP ("trying to iup in twilight zone");
if (!interpreter->glyph_zone)
TRAP ("iup without loaded glyph!");
/* Figure out what axis to interpolate in based on the opcode. */
if (opcode == 0x30)
mask = SFNT_POINT_TOUCHED_Y;
else
mask = SFNT_POINT_TOUCHED_X;
/* Now, for each contour, interpolate untouched points. */
point = 0;
for (i = 0; i < interpreter->glyph_zone->num_contours; ++i)
{
first_point = point;
end = interpreter->glyph_zone->contour_end_points[i];
if (point >= interpreter->glyph_zone->num_points
|| end >= interpreter->glyph_zone->num_points)
TRAP ("glyph contains out of bounds contour end point"
" data!");
sfnt_interpret_iup_1 (interpreter, first_point, end,
opcode, mask);
point = end + 1;
/* Skip the subsequent phantom points, which may end up
intermixed with contours inside a compound glyph. */
while (point < interpreter->glyph_zone->num_points
&& interpreter->glyph_zone->flags[point] & SFNT_POINT_PHANTOM)
point++;
}
}
/* Interpret an MIRP instruction with the specified OPCODE in
INTERPRETER. Pop a point in ZP1 and CVT index, and move the point
until its distance from RP0 in ZP0 is the same as in the control
value. If the point lies in the twilight zone, then ``create'' it
as well.
OPCODE contains a great many flags.
They are all described in the TrueType reference manual. */
static void
sfnt_interpret_mirp (struct sfnt_interpreter *interpreter,
uint32_t opcode)
{
uint32_t n;
uint32_t p;
sfnt_f26dot6 distance, delta, temp;
sfnt_f26dot6 current_projection, original_projection;
sfnt_f26dot6 x, y, org_x, org_y;
sfnt_f26dot6 rx, ry, org_rx, org_ry;
/* CVT index. */
n = POP ();
/* Point number. */
p = POP ();
/* Now get the distance from the CVT. */
if (n >= interpreter->cvt_size)
TRAP ("cvt index out of bounds");
distance = interpreter->cvt[n];
/* Test against the single width value. */
delta = sfnt_sub (distance,
interpreter->state.single_width_value);
if (delta < 0)
delta = -delta;
if (delta < interpreter->state.sw_cut_in)
{
/* Use the single width instead, as the CVT entry is too
small. */
if (distance >= 0)
distance = interpreter->state.single_width_value;
else
distance = -interpreter->state.single_width_value;
}
/* Load the reference point. */
sfnt_address_zp0 (interpreter, interpreter->state.rp0,
&rx, &ry, &org_rx, &org_ry);
/* Create the point in the twilight zone, should that be ZP1. */
if (!interpreter->state.zp1)
{
/* Since P hasn't been loaded yet, whether or not it is valid is
not known. */
sfnt_check_zp1 (interpreter, p);
interpreter->twilight_x[p] = rx;
interpreter->twilight_y[p] = ry;
temp = sfnt_mul_f2dot14 (interpreter->state.projection_vector.x,
distance);
temp = sfnt_add (temp, org_rx);
interpreter->twilight_original_x[p] = temp;
temp = sfnt_mul_f2dot14 (interpreter->state.projection_vector.y,
distance);
temp = sfnt_add (temp, org_ry);
interpreter->twilight_original_y[p] = temp;
}
/* Load P. */
sfnt_address_zp1 (interpreter, p, &x, &y, &org_x, &org_y);
/* If distance would be negative and auto_flip is on, flip it. */
original_projection = DUAL_PROJECT (org_x - org_rx,
org_y - org_ry);
current_projection = PROJECT (x - rx, y - ry);
if (interpreter->state.auto_flip)
{
if ((original_projection ^ distance) < 0)
distance = -distance;
}
/* Flag B means look at the cvt cut in and round the
distance. */
if (opcode & 4)
{
delta = sfnt_sub (distance, original_projection);
if (delta < 0)
delta = -delta;
if (delta > interpreter->state.cvt_cut_in)
distance = original_projection;
/* Now, round the distance. */
distance = sfnt_round_symmetric (interpreter, distance);
}
/* Flag C means look at the minimum distance. */
if (opcode & 8)
{
if (original_projection >= 0
&& distance < interpreter->state.minimum_distance)
distance = interpreter->state.minimum_distance;
else if (original_projection < 0
&& distance > -interpreter->state.minimum_distance)
distance = -interpreter->state.minimum_distance;
}
/* Finally, move the point. */
sfnt_move_zp1 (interpreter, p, 1,
sfnt_sub (distance, current_projection));
/* Set RP1 to RP0 and RP2 to the point. If flag 3 is set, also make
it RP0. */
interpreter->state.rp1 = interpreter->state.rp0;
interpreter->state.rp2 = p;
if (opcode & 16)
interpreter->state.rp0 = p;
}
/* Return the projection of the two points P1 and P2's original values
along the dual projection vector, with P1 inside ZP0 and P2 inside
ZP1. If this zone is the glyph zone and the outline positions of
those points are directly accessible, project their original
positions and scale the result with rounding, so as to prevent
rounding-introduced inaccuracies.
The scenario where such inaccuracies are significant is generally
where an Italic glyph is being instructed at small PPEM sizes,
during which a point moved by MDAP[rN] is within 1/64th of a
pixel's distance from a point on the grid, yet the measurements
taken between such a point and the reference point against which
the distance to move is computed is such that the position of the
point after applying their rounded values differs by one grid
coordinate from the font designer's intentions, either exaggerating
or neutralizing the slant of the stem to which it belongs.
This behavior applies only to MDRP (which see), although a similar
strategy is also applied while interpreting IP instructions. */
static sfnt_f26dot6
sfnt_project_zp1_zp0_org (struct sfnt_interpreter *interpreter,
uint32_t p1, uint32_t p2)
{
sfnt_fword x1, y1, x2, y2, projection;
struct sfnt_simple_glyph *simple;
sfnt_f26dot6 org_x1, org_y1, org_x2, org_y2;
/* Addressing the twilight zone, perhaps only partially. */
if (!interpreter->state.zp0
|| !interpreter->state.zp1
/* Not interpreting a glyph. */
|| !interpreter->glyph_zone
/* Not interpreting a simple glyph. */
|| !interpreter->glyph_zone->simple
/* P1 or P2 are phantom points. */
|| p1 >= interpreter->glyph_zone->simple->number_of_points
|| p2 >= interpreter->glyph_zone->simple->number_of_points)
goto project_normally;
simple = interpreter->glyph_zone->simple;
x1 = simple->x_coordinates[p1];
y1 = simple->y_coordinates[p1];
x2 = simple->x_coordinates[p2];
y2 = simple->y_coordinates[p2];
/* Compute the projection. */
projection = DUAL_PROJECT (x1 - x2, y1 - y2);
/* Return the projection, scaled with rounding. */
return sfnt_mul_fixed_round (projection, interpreter->scale);
project_normally:
sfnt_address_zp1 (interpreter, p1, NULL, NULL, &org_x1, &org_y1);
sfnt_address_zp0 (interpreter, p2, NULL, NULL, &org_x2, &org_y2);
return DUAL_PROJECT (org_x1 - org_x2, org_y1 - org_y2);
}
/* Interpret an MDRP instruction with the specified OPCODE in
INTERPRETER. Pop a point in ZP1, and move the point until its
distance from RP0 in ZP0 is the same as in the original outline.
This is almost like MIRP[abcde].
OPCODE contains a great many flags.
They are all described in the TrueType reference manual. */
static void
sfnt_interpret_mdrp (struct sfnt_interpreter *interpreter,
uint32_t opcode)
{
uint32_t p;
sfnt_f26dot6 distance, applied;
sfnt_f26dot6 current_projection;
sfnt_f26dot6 x, y, rx, ry;
/* Point number. */
p = POP ();
/* Load the points. */
sfnt_address_zp1 (interpreter, p, &x, &y, NULL, NULL);
sfnt_address_zp0 (interpreter, interpreter->state.rp0,
&rx, &ry, NULL, NULL);
/* Calculate the distance between P and rp0 prior to hinting. */
distance = sfnt_project_zp1_zp0_org (interpreter, p,
interpreter->state.rp0);
/* Calculate the distance between P and rp0 as of now in the hinting
process. */
current_projection = PROJECT (x - rx, y - ry);
/* Test against the single width value. */
if (interpreter->state.sw_cut_in > 0
&& distance < (interpreter->state.single_width_value
+ interpreter->state.sw_cut_in)
&& distance > (interpreter->state.single_width_value
- interpreter->state.sw_cut_in))
{
/* Use the single width instead, as the CVT entry is too
small. */
if (distance >= 0)
distance = interpreter->state.single_width_value;
else
distance = -interpreter->state.single_width_value;
}
/* Flag B implies that the distance should be rounded. The CVT cut
in is not taken into account by MDRP, contrary to earlier
presumptions. */
if (opcode & 4)
applied = sfnt_round_symmetric (interpreter, distance);
else
applied = distance;
/* Flag C means look at the minimum distance. */
if (opcode & 8)
{
/* Test the sign of the initial distance, but compare the
distance that will be applied in reality against the minimum
distance. */
if (distance >= 0
&& applied < interpreter->state.minimum_distance)
applied = interpreter->state.minimum_distance;
else if (distance < 0
&& applied > -interpreter->state.minimum_distance)
applied = -interpreter->state.minimum_distance;
}
/* Finally, move the point. */
sfnt_move_zp1 (interpreter, p, 1,
sfnt_sub (applied, current_projection));
/* Set RP1 to RP0 and RP2 to the point. If flag 3 is set, also make
it RP0. */
interpreter->state.rp1 = interpreter->state.rp0;
interpreter->state.rp2 = p;
if (opcode & 16)
interpreter->state.rp0 = p;
}
/* Execute the program now loaded into INTERPRETER.
WHY specifies why the interpreter is being run, and is used to
control the behavior of instructions such IDEF[] and FDEF[].
Transfer control to INTERPRETER->trap if interpretation is aborted
due to an error, and set INTERPRETER->trap_reason to a string
describing the error.
INTERPRETER->glyph_zone should be cleared before calling this
function. */
static void
sfnt_interpret_run (struct sfnt_interpreter *interpreter,
enum sfnt_interpreter_run_context why)
{
unsigned char opcode;
bool is_prep;
/* Determine whether or not this is the control value program. */
is_prep = (why == SFNT_RUN_CONTEXT_CONTROL_VALUE_PROGRAM);
#ifdef TEST
/* Allow testing control value program instructions as well. */
if (why == SFNT_RUN_CONTEXT_TEST)
is_prep = true;
#endif
while (interpreter->IP < interpreter->num_instructions)
{
opcode = interpreter->instructions[interpreter->IP];
#ifdef TEST
if (interpreter->run_hook)
interpreter->run_hook (interpreter);
#endif
switch (opcode)
{
case 0x00: /* SVTCA y */
SVTCAy ();
break;
case 0x01: /* SVTCA x */
SVTCAx ();
break;
case 0x02: /* SPvTCA y */
SPvTCAy ();
break;
case 0x03: /* SPvTCA x */
SPvTCAx ();
break;
case 0x04: /* SFvTCA y */
SFvTCAy ();
break;
case 0x05: /* SFvTCA x */
SFvTCAx ();
break;
case 0x06: /* SPvTL // */
case 0x07: /* SPvTL + */
SPVTL ();
break;
case 0x08: /* SFvTL // */
case 0x09: /* SFvTL + */
SFVTL ();
break;
case 0x0A: /* SPvFS */
SPVFS ();
break;
case 0x0B: /* SFvFS */
SFVFS ();
break;
case 0x0C: /* GPv */
GPV ();
break;
case 0x0D: /* GFv */
GFV ();
break;
case 0x0E: /* SFvTPv */
SFVTPV ();
break;
case 0x0F: /* ISECT */
ISECT ();
break;
case 0x10: /* SRP0 */
SRP0 ();
break;
case 0x11: /* SRP1 */
SRP1 ();
break;
case 0x12: /* SRP2 */
SRP2 ();
break;
case 0x13: /* SZP0 */
SZP0 ();
break;
case 0x14: /* SZP1 */
SZP1 ();
break;
case 0x15: /* SZP2 */
SZP2 ();
break;
case 0x16: /* SZPS */
SZPS ();
break;
case 0x17: /* SLOOP */
SLOOP ();
break;
case 0x18: /* RTG */
RTG ();
break;
case 0x19: /* RTHG */
RTHG ();
break;
case 0x1A: /* SMD */
SMD ();
break;
case 0x1B: /* ELSE */
ELSE ();
break;
case 0x1C: /* JMPR */
JMPR ();
break;
case 0x1D: /* SCVTCI */
SCVTCI ();
break;
case 0x1E: /* SSWCI */
SSWCI ();
break;
case 0x1F: /* SSW */
SSW ();
break;
case 0x20: /* DUP */
DUP ();
break;
case 0x21: /* POP */
POP ();
break;
case 0x22: /* CLEAR */
CLEAR ();
break;
case 0x23: /* SWAP */
SWAP ();
break;
case 0x24: /* DEPTH */
DEPTH ();
break;
case 0x25: /* CINDEX */
CINDEX ();
break;
case 0x26: /* MINDEX */
MINDEX ();
break;
case 0x27: /* ALIGNPTS */
ALIGNPTS ();
break;
case 0x28: /* RAW */
RAW ();
break;
case 0x29: /* UTP */
UTP ();
break;
case 0x2A: /* LOOPCALL */
LOOPCALL ();
break;
case 0x2B: /* CALL */
CALL ();
break;
case 0x2C: /* FDEF */
FDEF ();
break;
case 0x2D: /* ENDF */
ENDF ();
break;
case 0x2E: /* MDAP */
case 0x2F: /* MDAP */
MDAP ();
break;
case 0x30: /* IUP */
case 0x31: /* IUP */
IUP ();
break;
case 0x32: /* SHP */
case 0x33: /* SHP */
SHP ();
break;
case 0x34: /* SHC */
case 0x35: /* SHC */
SHC ();
break;
case 0x36: /* SHZ */
case 0x37: /* SHZ */
SHZ ();
break;
case 0x38: /* SHPIX */
SHPIX ();
break;
case 0x39: /* IP */
IP ();
break;
case 0x3A: /* MSIRP */
case 0x3B: /* MSIRP */
MSIRP ();
break;
case 0x3C: /* ALIGNRP */
ALIGNRP ();
break;
case 0x3D: /* RTDG */
RTDG ();
break;
case 0x3E: /* MIAP */
case 0x3F: /* MIAP */
MIAP ();
break;
case 0x40: /* NPUSHB */
NPUSHB ();
break;
case 0x41: /* NPUSHW */
NPUSHW ();
break;
case 0x42: /* WS */
WS ();
break;
case 0x43: /* RS */
RS ();
break;
case 0x44: /* WCVTP */
WCVTP ();
break;
case 0x45: /* RCVT */
RCVT ();
break;
case 0x46: /* GC */
case 0x47: /* GC */
GC ();
break;
case 0x48: /* SCFS */
SCFS ();
break;
case 0x49: /* MD */
case 0x4A: /* MD */
MD ();
break;
case 0x4B: /* MPPEM */
MPPEM ();
break;
case 0x4C: /* MPS */
MPS ();
break;
case 0x4D: /* FLIPON */
FLIPON ();
break;
case 0x4E: /* FLIPOFF */
FLIPOFF ();
break;
case 0x4F: /* DEBUG */
DEBUG ();
break;
case 0x50: /* LT */
LT ();
break;
case 0x51: /* LTEQ */
LTEQ ();
break;
case 0x52: /* GT */
GT ();
break;
case 0x53: /* GTEQ */
GTEQ ();
break;
case 0x54: /* EQ */
EQ ();
break;
case 0x55: /* NEQ */
NEQ ();
break;
case 0x56: /* ODD */
ODD ();
break;
case 0x57: /* EVEN */
EVEN ();
break;
case 0x58: /* IF */
IF ();
break;
case 0x59: /* EIF */
EIF ();
break;
case 0x5A: /* AND */
AND ();
break;
case 0x5B: /* OR */
OR ();
break;
case 0x5C: /* NOT */
NOT ();
break;
case 0x5D: /* DELTAP1 */
DELTAP1 ();
break;
case 0x5E: /* SDB */
SDB ();
break;
case 0x5F: /* SDS */
SDS ();
break;
case 0x60: /* ADD */
ADD ();
break;
case 0x61: /* SUB */
SUB ();
break;
case 0x62: /* DIV */
DIV ();
break;
case 0x63: /* MUL */
MUL ();
break;
case 0x64: /* ABS */
ABS ();
break;
case 0x65: /* NEG */
NEG ();
break;
case 0x66: /* FLOOR */
FLOOR ();
break;
case 0x67: /* CEILING */
CEILING ();
break;
case 0x68: /* ROUND */
case 0x69: /* ROUND */
case 0x6A: /* ROUND */
case 0x6B: /* ROUND */
ROUND ();
break;
case 0x6C: /* NROUND */
case 0x6D: /* NROUND */
case 0x6E: /* NRRUND */
case 0x6F: /* NROUND */
NROUND ();
break;
case 0x70: /* WCVTF */
WCVTF ();
break;
case 0x71: /* DELTAP2 */
DELTAP2 ();
break;
case 0x72: /* DELTAP3 */
DELTAP3 ();
break;
case 0x73: /* DELTAC1 */
DELTAC1 ();
break;
case 0x74: /* DELTAC2 */
DELTAC2 ();
break;
case 0x75: /* DELTAC3 */
DELTAC3 ();
break;
case 0x76: /* SROUND */
SROUND ();
break;
case 0x77: /* S45Round */
S45ROUND ();
break;
case 0x78: /* JROT */
JROT ();
break;
case 0x79: /* JROF */
JROF ();
break;
case 0x7A: /* ROFF */
ROFF ();
break;
case 0x7B: /* ILLEGAL_INSTRUCTION */
ILLEGAL_INSTRUCTION ();
break;
case 0x7C: /* RUTG */
RUTG ();
break;
case 0x7D: /* RDTG */
RDTG ();
break;
case 0x7E: /* SANGW */
SANGW ();
break;
case 0x7F: /* AA */
AA ();
break;
case 0x80: /* FLIPPT */
FLIPPT ();
break;
case 0x81: /* FLIPRGON */
FLIPRGON ();
break;
case 0x82: /* FLIPRGOFF */
FLIPRGOFF ();
break;
case 0x83: /* RMVT */
case 0x84: /* WMVT */
NOT_IMPLEMENTED ();
break;
case 0x85: /* SCANCTRL */
SCANCTRL ();
break;
case 0x86: /* SDPVTL */
case 0x87: /* SDPVTL */
SDPVTL ();
break;
case 0x88: /* GETINFO */
GETINFO ();
break;
case 0x89: /* IDEF */
IDEF ();
break;
case 0x8A: /* ROLL */
ROLL ();
break;
case 0x8B: /* MAX */
_MAX ();
break;
case 0x8C: /* MIN */
_MIN ();
break;
/* Scan or dropout control is not implemented. Instead, 256
grays are used to display pixels which are partially
turned on. */
case 0x8D: /* SCANTYPE */
SCANTYPE ();
break;
case 0x8E: /* INSTCTRL */
INSTCTRL ();
break;
case 0x8F: /* ADJUST */
case 0x90: /* ADJUST */
NOT_IMPLEMENTED ();
break;
case 0x91: /* GXAXIS */
GXAXIS ();
break;
default:
if (opcode >= 0xE0) /* MIRP */
{
MIRP ();
}
else if (opcode >= 0xC0) /* MDRP */
{
MDRP ();
}
else if (opcode >= 0xB8) /* PUSHW */
{
PUSHW ();
}
else if (opcode >= 0xB0) /* PUSHB */
{
PUSHB ();
}
else
NOT_IMPLEMENTED ();
}
next_instruction:
/* In the case of an NPUSHB or NPUSHW instruction,
interpreter->IP has only been increased to skip over the
extra bytes, and not the byte containing the instruction
itself. */
interpreter->IP++;
/* This label is used by instructions to continue without
incrementing IP. It is used by instructions which set IP
themselves, such as ELSE, IF, FDEF, IDEF and JMPR. */
skip_step:
continue;
}
}
/* Execute the font program FPGM using INTERPRETER.
This must only be called once per interpreter, else behavior is
undefined.
Value is NULL upon success, else it is a string describing the
reason for failure.
The caller must save the graphics state after interpreting the font
program and restore it prior to instructing each glyph. */
TEST_STATIC const char *
sfnt_interpret_font_program (struct sfnt_interpreter *interpreter,
struct sfnt_fpgm_table *fpgm)
{
if (setjmp (interpreter->trap))
return interpreter->trap_reason;
/* Set up the interpreter to evaluate the font program. */
interpreter->IP = 0;
interpreter->SP = interpreter->stack;
interpreter->instructions = fpgm->instructions;
interpreter->num_instructions = fpgm->num_instructions;
interpreter->glyph_zone = NULL;
sfnt_interpret_run (interpreter, SFNT_RUN_CONTEXT_FONT_PROGRAM);
return NULL;
}
/* Execute the control value program PREP using INTERPRETER.
Return NULL and the graphics state after the execution of the
program in *STATE, or a string describing the reason for a failure
to interpret the program.
The caller must save the graphics state after interpreting the
control value program and restore it prior to instructing each
glyph. */
TEST_STATIC const char *
sfnt_interpret_control_value_program (struct sfnt_interpreter *interpreter,
struct sfnt_prep_table *prep,
struct sfnt_graphics_state *state)
{
if (setjmp (interpreter->trap))
return interpreter->trap_reason;
/* Set up the interpreter to evaluate the control value program. */
interpreter->IP = 0;
interpreter->SP = interpreter->stack;
interpreter->instructions = prep->instructions;
interpreter->num_instructions = prep->num_instructions;
interpreter->glyph_zone = NULL;
sfnt_interpret_run (interpreter,
SFNT_RUN_CONTEXT_CONTROL_VALUE_PROGRAM);
/* If instruct_control & 2, then changes to the graphics state made
in this program should be reverted. */
if (interpreter->state.instruct_control & 2)
sfnt_init_graphics_state (&interpreter->state);
else
{
/* And even if not, reset the following graphics state
variables, to which both the Apple and MS scalers don't
permit modifications from the preprogram.
Not only is such reversion undocumented, it is also
inefficient, for modern fonts at large only move points on
the Y axis. As such, these fonts must issue a redundant
SVTCA[Y] instruction within each glyph program, in place of
initializing the projection and freedom vectors once and for
all in prep. Unfortunately many fonts which do instruct on
the X axis now rely on this ill-conceived behavior, so Emacs
must, reluctantly, follow suit. */
interpreter->state.dual_projection_vector.x = 040000; /* 1.0 */
interpreter->state.dual_projection_vector.y = 0;
interpreter->state.freedom_vector.x = 040000; /* 1.0 */
interpreter->state.freedom_vector.y = 0;
interpreter->state.projection_vector.x = 040000; /* 1.0 */
interpreter->state.projection_vector.y = 0;
interpreter->state.rp0 = 0;
interpreter->state.rp1 = 0;
interpreter->state.rp2 = 0;
interpreter->state.zp0 = 1;
interpreter->state.zp1 = 1;
interpreter->state.zp2 = 1;
interpreter->state.loop = 1;
/* Validate the graphics state. */
sfnt_validate_gs (&interpreter->state);
}
/* Save the graphics state upon success. */
memcpy (state, &interpreter->state, sizeof *state);
return NULL;
}
/* Glyph hinting. The routines here perform hinting on simple and
compound glyphs.
In order to keep the hinting mechanism separate from the rest of
the code, the routines here perform outline decomposition and
scaling separately. It might be nice to fix that in the
future. */
/* Instructed glyph outline decomposition. This is separate from
sfnt_decompose_glyph because this file should be able to be built
with instructions disabled. */
/* Decompose OUTLINE, an instructed outline, into its individual
components.
Call MOVE_TO to move to a specific location. For each line
encountered, call LINE_TO to draw a line to that location. For
each spline encountered, call CURVE_TO to draw the curves
comprising the spline. Call each of those functions with 16.16
fixed point coordinates.
Call all functions with DCONTEXT as an argument.
The winding rule used to fill the resulting lines is described in
chapter 2 of the TrueType reference manual, under the heading
"distinguishing the inside from the outside of a glyph."
Value is 0 upon success, or some non-zero value upon failure, which
can happen if the glyph is invalid. */
static int
sfnt_decompose_instructed_outline (struct sfnt_instructed_outline *outline,
sfnt_move_to_proc move_to,
sfnt_line_to_proc line_to,
sfnt_curve_to_proc curve_to,
void *dcontext)
{
size_t here, last, n;
if (!outline->num_contours)
return 0;
here = 0;
for (n = 0; n < outline->num_contours; ++n)
{
/* here is the first index into the glyph's point arrays
belonging to the contour in question. last is the index
of the last point in the contour. */
last = outline->contour_end_points[n];
/* Make sure here and last make sense. */
if (here > last || last >= outline->num_points)
goto fail;
if (sfnt_decompose_glyph_2 (here, last, move_to,
line_to, curve_to, dcontext,
outline->x_points,
outline->y_points,
outline->flags, 1024))
goto fail;
/* Move forward to the start of the next contour. */
here = last + 1;
/* here may be a phantom point when outlining a compound glyph,
as they can have phantom points mixed in with contours.
When that happens, skip past all the phantom points. */
while (here < outline->num_points
&& outline->flags[here] & SFNT_POINT_PHANTOM)
here++;
}
return 0;
fail:
return 1;
}
/* Decompose and build an outline for the specified instructed outline
INSTRUCTED. Return the outline data with a refcount of 0 upon
success, and the advance width of the instructed glyph in
*ADVANCE_WIDTH, or NULL upon failure.
This function is not reentrant. */
TEST_STATIC struct sfnt_glyph_outline *
sfnt_build_instructed_outline (struct sfnt_instructed_outline *instructed,
sfnt_fixed *advance_width)
{
struct sfnt_glyph_outline *outline;
int rc;
sfnt_f26dot6 x1, x2;
memset (&build_outline_context, 0, sizeof build_outline_context);
/* Allocate the outline now with enough for 44 words at the end. */
outline = xmalloc (sizeof *outline + 40 * sizeof (*outline->outline));
outline->outline_size = 40;
outline->outline_used = 0;
outline->refcount = 0;
outline->outline
= (struct sfnt_glyph_outline_command *) (outline + 1);
/* Clear outline bounding box. */
outline->xmin = 0;
outline->ymin = 0;
outline->xmax = 0;
outline->ymax = 0;
/* Set up the context. */
build_outline_context.outline = outline;
build_outline_context.factor = 0177777;
/* Start decomposing. */
rc = sfnt_decompose_instructed_outline (instructed,
sfnt_move_to_and_build,
sfnt_line_to_and_build,
sfnt_curve_to_and_build,
NULL);
/* Synchronize the outline object with what might have changed
inside sfnt_decompose_glyph. */
outline = build_outline_context.outline;
/* Finally, obtain the origin point of the glyph after it has been
instructed. */
if (instructed->num_points > 1)
{
x1 = instructed->x_points[instructed->num_points - 2];
x2 = instructed->x_points[instructed->num_points - 1];
/* Convert the origin point to a 16.16 fixed point number. */
outline->origin = x1 * 1024;
/* Do the same for the advance width. */
*advance_width = (x2 - x1) * 1024;
}
else
{
/* Phantom points are absent from this outline, which is
impossible. */
*advance_width = 0;
outline->origin = 0;
}
if (rc)
{
xfree (outline);
return NULL;
}
return outline;
}
/* Compute phantom points for the specified glyph GLYPH. Use the
unscaled metrics specified in METRICS, and the 16.16 fixed point
scale SCALE.
Place the X and Y coordinates of the first phantom point in *X1 and
*Y1, and those of the second phantom point in *X2 and *Y2.
Place the unrounded X coordinates of both phantom points in *S1 and
*S2 respectively. */
static void
sfnt_compute_phantom_points (struct sfnt_glyph *glyph,
struct sfnt_glyph_metrics *metrics,
sfnt_fixed scale,
sfnt_f26dot6 *x1, sfnt_f26dot6 *y1,
sfnt_f26dot6 *x2, sfnt_f26dot6 *y2,
sfnt_f26dot6 *s1, sfnt_f26dot6 *s2)
{
sfnt_fword f1, f2;
/* Two ``phantom points'' are appended to each outline by the scaler
prior to instruction interpretation. One of these points
represents the left-side bearing distance from xmin, while the
other represents the advance width. Both are then used after the
hinting process to ensure that the reported glyph metrics are
consistent with the instructed outline. */
/* First compute both values in fwords. */
f1 = glyph->xmin - metrics->lbearing;
f2 = f1 + metrics->advance;
/* Apply the metrics distortion. */
f1 += glyph->origin_distortion;
f2 += glyph->advance_distortion;
/* Next, scale both up. */
*s1 = sfnt_mul_f26dot6_fixed (f1, scale);
*s2 = sfnt_mul_f26dot6_fixed (f2, scale);
/* While not expressly provided in the manual, the phantom points
(at times termed the advance and origin points) represent pixel
coordinates within the raster, and are therefore rounded. */
*x1 = sfnt_round_f26dot6 (*s1);
*x2 = sfnt_round_f26dot6 (*s2);
/* Clear y1 and y2. */
*y1 = 0;
*y2 = 0;
}
/* Load the simple glyph GLYPH into the specified INTERPRETER, scaling
it up by INTERPRETER's scale, and run its glyph program if
present. Use the unscaled metrics specified in METRICS.
Upon success, return NULL and the resulting points and contours in
*VALUE. Else, value is the reason interpretation failed. */
TEST_STATIC const char *
sfnt_interpret_simple_glyph (struct sfnt_glyph *glyph,
struct sfnt_interpreter *interpreter,
struct sfnt_glyph_metrics *metrics,
struct sfnt_instructed_outline **value)
{
size_t zone_size, temp, outline_size, i;
struct sfnt_interpreter_zone *zone;
struct sfnt_interpreter_zone *volatile preserved_zone;
sfnt_f26dot6 phantom_point_1_y;
sfnt_f26dot6 phantom_point_2_y;
sfnt_f26dot6 tem;
volatile bool zone_was_allocated;
struct sfnt_instructed_outline *outline;
zone_size = 0;
zone_was_allocated = false;
/* Calculate the size of the zone structure. */
if (ckd_mul (&temp, glyph->simple->number_of_points + 2,
sizeof *zone->x_points * 4)
|| ckd_add (&zone_size, zone_size, temp)
|| ckd_mul (&temp, glyph->number_of_contours,
sizeof *zone->contour_end_points)
|| ckd_add (&zone_size, zone_size, temp)
|| ckd_mul (&temp, glyph->simple->number_of_points + 2,
sizeof *zone->flags)
|| ckd_add (&zone_size, zone_size, temp)
|| ckd_add (&zone_size, zone_size, sizeof *zone))
return "Glyph exceeded maximum permissible size";
/* Don't use malloc if possible. */
if (zone_size <= 1024 * 16)
zone = alloca (zone_size);
else
{
zone = xmalloc (zone_size);
zone_was_allocated = true;
}
/* Now load the zone with data. */
zone->num_points = glyph->simple->number_of_points + 2;
zone->num_contours = glyph->number_of_contours;
zone->contour_end_points = (size_t *) (zone + 1);
zone->x_points = (sfnt_f26dot6 *) (zone->contour_end_points
+ zone->num_contours);
zone->x_current = zone->x_points + zone->num_points;
zone->y_points = zone->x_current + zone->num_points;
zone->y_current = zone->y_points + zone->num_points;
zone->flags = (unsigned char *) (zone->y_current
+ zone->num_points);
zone->simple = glyph->simple;
/* Load x_points and x_current. */
for (i = 0; i < glyph->simple->number_of_points; ++i)
{
/* Load the fword. */
tem = glyph->simple->x_coordinates[i];
/* Scale that fword. */
tem = sfnt_mul_f26dot6_fixed (tem, interpreter->scale);
/* Set x_points and x_current. */
zone->x_points[i] = tem;
zone->x_current[i] = tem;
}
/* Compute and load phantom points. */
sfnt_compute_phantom_points (glyph, metrics, interpreter->scale,
&zone->x_current[i], &phantom_point_1_y,
&zone->x_current[i + 1], &phantom_point_2_y,
/* Phantom points are rounded to the
pixel grid once they are inserted
into the glyph zone, but the
original coordinates must remain
untouched, as fonts rely on this to
interpolate points by this
scale. */
&zone->x_points[i], &zone->x_points[i + 1]);
/* Load y_points and y_current, along with flags. */
for (i = 0; i < glyph->simple->number_of_points; ++i)
{
/* Load the fword. */
tem = glyph->simple->y_coordinates[i];
/* Scale that fword. Make sure not to round Y, as this could
lead to Y spilling over to the next line. */
tem = sfnt_mul_f26dot6_fixed (tem, interpreter->scale);
/* Set y_points and y_current. */
zone->y_points[i] = tem;
zone->y_current[i] = tem;
/* Set flags. */
zone->flags[i] = (glyph->simple->flags[i]
& ~SFNT_POINT_TOUCHED_BOTH);
/* Make sure to clear the phantom points flag. */
zone->flags[i] &= ~SFNT_POINT_PHANTOM;
}
/* Load phantom points. */
zone->y_points[i] = phantom_point_1_y;
zone->y_points[i + 1] = phantom_point_2_y;
zone->y_current[i] = phantom_point_1_y;
zone->y_current[i + 1] = phantom_point_2_y;
/* Load phantom point flags. */
zone->flags[i] = SFNT_POINT_PHANTOM;
zone->flags[i + 1] = SFNT_POINT_PHANTOM;
/* Load contour end points. */
for (i = 0; i < zone->num_contours; ++i)
zone->contour_end_points[i]
= glyph->simple->end_pts_of_contours[i];
/* Load the glyph program. */
interpreter->IP = 0;
interpreter->SP = interpreter->stack;
interpreter->instructions = glyph->simple->instructions;
interpreter->num_instructions = glyph->simple->instruction_length;
interpreter->glyph_zone = zone;
/* Copy zone over to this volatile variable. */
preserved_zone = zone;
if (setjmp (interpreter->trap))
{
if (zone_was_allocated)
xfree (preserved_zone);
interpreter->glyph_zone = NULL;
return interpreter->trap_reason;
}
sfnt_interpret_run (interpreter, SFNT_RUN_CONTEXT_GLYPH_PROGRAM);
interpreter->glyph_zone = NULL;
/* Move preserved_zone back to zone. */
zone = preserved_zone;
/* Now that the program has been run, build the scaled outline. */
outline_size = sizeof (*outline);
outline_size += (zone->num_contours
* sizeof *outline->contour_end_points);
outline_size += (zone->num_points
* sizeof *outline->x_points * 2);
outline_size += zone->num_points;
/* Allocate the outline. */
outline = xmalloc (outline_size);
outline->num_points = zone->num_points;
outline->num_contours = zone->num_contours;
outline->contour_end_points = (size_t *) (outline + 1);
outline->x_points = (sfnt_f26dot6 *) (outline->contour_end_points
+ outline->num_contours);
outline->y_points = outline->x_points + outline->num_points;
outline->flags = (unsigned char *) (outline->y_points
+ outline->num_points);
/* Copy over the contour endpoints, points, and flags. */
memcpy (outline->contour_end_points, zone->contour_end_points,
zone->num_contours * sizeof *outline->contour_end_points);
memcpy (outline->x_points, zone->x_current,
zone->num_points * sizeof *outline->x_points);
memcpy (outline->y_points, zone->y_current,
zone->num_points * sizeof *outline->y_points);
memcpy (outline->flags, zone->flags, zone->num_points);
/* Free the zone if necessary. */
if (zone_was_allocated)
xfree (zone);
/* Return the outline and NULL. */
*value = outline;
return NULL;
}
/* Apply the transform in the compound glyph component COMPONENT to
the array of points of length NUM_COORDINATES given as X and Y.
Treat X and Y as arrays of 26.6 fixed point values.
Also, apply the 26.6 fixed point offsets X_OFF and Y_OFF to each X
and Y coordinate after the transforms in COMPONENT are
effected. */
static void
sfnt_transform_f26dot6 (struct sfnt_compound_glyph_component *component,
sfnt_f26dot6 *restrict x, sfnt_f26dot6 *restrict y,
size_t num_coordinates,
sfnt_f26dot6 x_off, sfnt_f26dot6 y_off)
{
double m1, m2, m3;
double m4, m5, m6;
size_t i;
if (component->flags & 010) /* WE_HAVE_A_SCALE */
{
m1 = component->u.scale / 16384.0;
m2 = m3 = m4 = 0;
m5 = component->u.scale / 16384.0;
m6 = 0;
}
else if (component->flags & 0100) /* WE_HAVE_AN_X_AND_Y_SCALE */
{
m1 = component->u.a.xscale / 16384.0;
m2 = m3 = m4 = 0;
m5 = component->u.a.yscale / 16384.0;
m6 = 0;
}
else if (component->flags & 0200) /* WE_HAVE_A_TWO_BY_TWO */
{
m1 = component->u.b.xscale / 16384.0;
m2 = component->u.b.scale01 / 16384.0;
m3 = 0;
m4 = component->u.b.scale10 / 16384.0;
m5 = component->u.b.yscale / 16384.0;
m6 = 0;
}
else /* No scale, just apply x_off and y_off. */
{
if (x_off || y_off)
{
for (i = 0; i < num_coordinates; ++i)
x[i] += x_off, y[i] += y_off;
}
return;
}
m3 = x_off;
m6 = y_off;
/* Apply the specified affine transformation.
A transform looks like:
M1 M2 M3 X
M4 M5 M6 * Y
=
M1*X + M2*Y + M3*1 = X1
M4*X + M5*Y + M6*1 = Y1
(In most transforms, there is another row at the bottom for
mathematical reasons. Since Z1 is always 1.0, the row is simply
implied to be 0 0 1, because 0 * x + 0 * y + 1 * 1 = 1.0. See
the definition of matrix3x3 in image.c for some more explanations
about this.) */
for (i = 0; i < num_coordinates; ++i)
{
x[i] = m1 * x[i] + m2 * y[i] + m3 * 1;
y[i] = m4 * x[i] + m5 * y[i] + m6 * 1;
}
}
/* Internal helper for sfnt_interpret_compound_glyph_3.
Instruct the compound glyph GLYPH using INTERPRETER after all of
its components have been instructed. Save the resulting points
within CONTEXT, and set its phantom point fields to match as well.
Use the unscaled METRICS to compute the phantom points of this
glyph.
CONTEXT contains the points and contours of this compound glyph,
numbered starting from BASE_INDEX and BASE_CONTOUR respectively.
In addition, CONTEXT also contains two additional ``phantom
points'' supplying the left and right side bearings of GLYPH.
S1 and S2 are the unrounded values of the last two phantom points,
which supply the original values saved into the glyph zone. In
practical terms, they are set as the last two values of the glyph
zone's original position array.
Value is NULL upon success, or a description of the error upon
failure. */
static const char *
sfnt_interpret_compound_glyph_2 (struct sfnt_glyph *glyph,
struct sfnt_interpreter *interpreter,
struct sfnt_compound_glyph_context *context,
size_t base_index, size_t base_contour,
struct sfnt_glyph_metrics *metrics,
sfnt_f26dot6 s1, sfnt_f26dot6 s2)
{
size_t num_points, num_contours, i;
size_t zone_size, temp;
struct sfnt_interpreter_zone *zone;
struct sfnt_interpreter_zone *volatile preserved_zone;
volatile bool zone_was_allocated;
sfnt_f26dot6 *x_base, *y_base;
/* Figure out how many points and contours there are to instruct. A
minimum of two points must be present, namely: the origin and
advance phantom points. */
num_points = context->num_points - base_index;
num_contours = context->num_end_points - base_contour;
assert (num_points >= 2);
/* Nothing to instruct! */
if (!num_points && !num_contours)
return NULL;
/* Build the zone. First, calculate the size of the zone
structure. */
zone_size = 0;
zone_was_allocated = false;
if (ckd_mul (&temp, num_points + 2, sizeof *zone->x_points * 4)
|| ckd_add (&zone_size, zone_size, temp)
|| ckd_mul (&temp, num_contours, sizeof *zone->contour_end_points)
|| ckd_add (&zone_size, zone_size, temp)
|| ckd_mul (&temp, num_points + 2, sizeof *zone->flags)
|| ckd_add (&zone_size, zone_size, temp)
|| ckd_add (&zone_size, zone_size, sizeof *zone))
return "Glyph exceeded maximum permissible size";
/* Don't use malloc if possible. */
if (zone_size <= 1024 * 16)
zone = alloca (zone_size);
else
{
zone = xmalloc (zone_size);
zone_was_allocated = true;
}
/* Now load the zone with data. */
zone->num_points = num_points;
zone->num_contours = num_contours;
zone->contour_end_points = (size_t *) (zone + 1);
zone->x_points = (sfnt_f26dot6 *) (zone->contour_end_points
+ zone->num_contours);
zone->x_current = zone->x_points + zone->num_points;
zone->y_points = zone->x_current + zone->num_points;
zone->y_current = zone->y_points + zone->num_points;
zone->flags = (unsigned char *) (zone->y_current
+ zone->num_points);
zone->simple = NULL;
/* Copy and renumber all contour end points to start from
base_index. */
for (i = 0; i < zone->num_contours; ++i)
zone->contour_end_points[i]
= (context->contour_end_points[base_contour + i]
- base_index);
/* Now copy over x_points, x_current, y_points and y_current. */
for (i = 0; i < num_points; ++i)
{
zone->x_current[i] = context->x_coordinates[i + base_index];
zone->x_points[i] = context->x_coordinates[i + base_index];
}
for (i = 0; i < num_points; ++i)
{
zone->y_current[i] = context->y_coordinates[i + base_index];
zone->y_points[i] = context->y_coordinates[i + base_index];
/* Set flags. */
zone->flags[i] = (context->flags[i + base_index]
& ~SFNT_POINT_TOUCHED_BOTH);
}
/* Copy S1 and S2 into the glyph zone. */
assert (num_points >= 2);
zone->x_points[num_points - 1] = s2;
zone->x_points[num_points - 2] = s1;
/* Load the compound glyph program. */
interpreter->IP = 0;
interpreter->SP = interpreter->stack;
interpreter->instructions = glyph->compound->instructions;
interpreter->num_instructions = glyph->compound->instruction_length;
interpreter->glyph_zone = zone;
/* Copy zone over to this volatile variable. */
preserved_zone = zone;
if (setjmp (interpreter->trap))
{
if (zone_was_allocated)
xfree (preserved_zone);
interpreter->glyph_zone = NULL;
return interpreter->trap_reason;
}
sfnt_interpret_run (interpreter, SFNT_RUN_CONTEXT_GLYPH_PROGRAM);
interpreter->glyph_zone = NULL;
/* Move preserved_zone back to zone. */
zone = preserved_zone;
/* Now copy the instructed points back, and add the two phantom
points to the end. */
for (i = 0; i < num_points; ++i)
{
context->x_coordinates[base_index + i] = zone->x_current[i];
context->y_coordinates[base_index + i] = zone->y_current[i];
}
/* Return the phantom points after instructing completes to the
context's coordinate arrays. */
x_base = &context->x_coordinates[i - 2];
y_base = &context->y_coordinates[i - 2];
x_base[0] = zone->x_current[num_points - 2];
x_base[1] = zone->x_current[num_points - 1];
y_base[0] = zone->y_current[num_points - 2];
y_base[1] = zone->y_current[num_points - 1];
context->phantom_point_1_x = x_base[0];
context->phantom_point_1_y = y_base[0];
context->phantom_point_1_s = x_base[0];
context->phantom_point_2_x = x_base[1];
context->phantom_point_2_y = y_base[1];
context->phantom_point_2_s = x_base[1];
/* Free the zone if needed. */
if (zone_was_allocated)
xfree (zone);
return NULL;
}
/* Internal helper for sfnt_interpret_compound_glyph.
RECURSION_COUNT is the number of times this function has called itself.
METRICS are the unscaled metrics of this compound glyph.
Other arguments mean the same as they do in
`sfnt_interpret_compound_glyph'. Value is NULL upon success, or a
string describing the reason for failure. */
static const char *
sfnt_interpret_compound_glyph_1 (struct sfnt_glyph *glyph,
struct sfnt_interpreter *interpreter,
struct sfnt_graphics_state *state,
struct sfnt_compound_glyph_context *context,
sfnt_get_glyph_proc get_glyph,
sfnt_free_glyph_proc free_glyph,
struct sfnt_hmtx_table *hmtx,
struct sfnt_hhea_table *hhea,
struct sfnt_maxp_table *maxp,
struct sfnt_glyph_metrics *metrics,
int recursion_count,
void *dcontext)
{
struct sfnt_glyph *subglyph;
int i, j, rc;
const char *error;
bool need_free;
struct sfnt_compound_glyph_component *component;
sfnt_f26dot6 x, y, xtemp, ytemp;
size_t point, point2;
size_t last_point, number_of_contours;
sfnt_f26dot6 *x_base, *y_base;
size_t *contour_base;
unsigned char *flags_base;
size_t base_index, contour_start, base_contour;
bool defer_offsets;
struct sfnt_instructed_outline *value;
struct sfnt_glyph_metrics sub_metrics;
sfnt_f26dot6 pp1x, pp1y, pp1s;
sfnt_f26dot6 pp2x, pp2y, pp2s;
error = NULL;
/* Set up the base index. This is the index from where on point
renumbering starts.
In other words, point 0 in this glyph will be 0 + base_index,
point 1 will be 1 + base_index, and so on. */
base_index = context->num_points;
/* And this is the index of the first contour in this glyph. */
base_contour = context->num_end_points;
/* Prevent infinite loops. Simply limit the level of nesting to the
maximum valid value of `max_component_depth', which is 16. */
if (recursion_count > 16)
return "Excessive recursion in compound glyph data";
/* Pacify -Wmaybe-uninitialized. */
point = point2 = 0;
/* Compute phantom points for this glyph here. They will be
subsequently overridden if a component glyph's metrics must be
used instead. */
sfnt_compute_phantom_points (glyph, metrics, interpreter->scale,
&context->phantom_point_1_x,
&context->phantom_point_1_y,
&context->phantom_point_2_x,
&context->phantom_point_2_y,
&context->phantom_point_1_s,
&context->phantom_point_2_s);
for (j = 0; j < glyph->compound->num_components; ++j)
{
/* Look up the associated subglyph. */
component = &glyph->compound->components[j];
subglyph = get_glyph (component->glyph_index,
dcontext, &need_free);
if (!subglyph)
return "Failed to obtain component glyph";
/* Don't defer offsets. This variable is set if the component
glyph is a compound glyph that is anchored to a previously
decomposed point, and needs its coordinates adjusted after
decomposition completes. */
defer_offsets = false;
/* Record the size of the point array before expansion. This
will be the base to apply to all points coming from this
subglyph. */
contour_start = context->num_points;
/* Compute the offset for the component. */
if (component->flags & 02) /* ARGS_ARE_XY_VALUES */
{
/* Component offsets are X/Y values as opposed to points
GLYPH. */
if (!(component->flags & 01)) /* ARG_1_AND_2_ARE_WORDS */
{
/* X and Y are signed bytes. */
x = component->argument1.b;
y = component->argument2.b;
}
else
{
/* X and Y are signed words. */
x = component->argument1.d;
y = component->argument2.d;
}
/* Now convert X and Y into device coordinates. */
x = sfnt_mul_f26dot6_fixed (x, interpreter->scale);
y = sfnt_mul_f26dot6_fixed (y, interpreter->scale);
/* If there is some kind of scale and component offsets are
scaled, then apply the transform to the offset. */
if (component->flags & 04000) /* SCALED_COMPONENT_OFFSET */
sfnt_transform_f26dot6 (component, &x, &y, 1,
0, 0);
if (component->flags & 04) /* ROUND_XY_TO_GRID */
{
x = sfnt_round_f26dot6 (x);
y = sfnt_round_f26dot6 (y);
}
}
else
{
/* The offset is determined by matching a point location in
a preceding component with a point location in the
current component. The index of the point in the
previous component is established by adding
component->argument1.a or component->argument1.c to
point. argument2 contains the index of the point in the
current component. */
if (!(component->flags & 01)) /* ARG_1_AND_2_ARE_WORDS */
{
point = base_index + component->argument1.a;
point2 = component->argument2.a;
}
else
{
point = base_index + component->argument1.c;
point2 = component->argument2.c;
}
/* Now, check that the anchor point specified lies inside
the glyph. */
if (point >= contour_start)
{
if (need_free)
free_glyph (subglyph, dcontext);
return "Invalid anchor reference point";
}
if (!subglyph->compound)
{
/* Detect invalid child anchor points within simple
glyphs in advance. */
if (point2 >= subglyph->simple->number_of_points + 2)
{
if (need_free)
free_glyph (subglyph, dcontext);
return "Invalid component anchor point";
}
}
/* First, set offsets to 0, because it is not yet possible
to ascertain the position of the anchor point in the
child. That position cannot be established prior to the
completion of grid-fitting. */
x = 0;
y = 0;
/* Set a flag which indicates that offsets must be resolved
from the child glyph after it is loaded, but before it is
incorporated into the parent glyph. */
defer_offsets = true;
}
/* Obtain the glyph metrics. If doing so fails, then cancel
decomposition. */
if (sfnt_lookup_glyph_metrics (component->glyph_index,
&sub_metrics, hmtx, hhea,
maxp))
{
if (need_free)
free_glyph (subglyph, dcontext);
return "Failed to obtain component metrics";
}
if (subglyph->simple)
{
/* Simple subglyph. Copy over the points and contours,
then transform and instruct them.
Skip this step for glyphs without contours. */
if (subglyph->number_of_contours)
{
/* Now instruct the simple glyph, and copy it over,
including the two phantom points at the end. */
interpreter->state = *state;
error = sfnt_interpret_simple_glyph (subglyph, interpreter,
&sub_metrics, &value);
/* Cancel instructing if an error occurs. */
if (error)
{
if (need_free)
free_glyph (subglyph, dcontext);
return error;
}
/* Figure out how many more points and contours are
needed. While phantom points are not included within
the outline ultimately produced, they are temporarily
appended to the outline here, so as to enable
defer_offsets below to refer to them. */
assert (value->num_points >= 2);
last_point = value->num_points - 2;
number_of_contours = value->num_contours;
/* Grow various arrays. */
rc = sfnt_expand_compound_glyph_context (context,
/* Number of
new contours
required. */
number_of_contours,
/* Number of new
points
required. */
last_point,
&x_base,
&y_base,
&flags_base,
&contour_base);
if (rc)
{
xfree (value);
if (need_free)
free_glyph (subglyph, dcontext);
return "Failed to grow arrays";
}
/* Copy the values in VALUE into the context and free
VALUE, including phantom points. */
for (i = 0; i < last_point; ++i)
{
x_base[i] = value->x_points[i];
y_base[i] = value->y_points[i];
flags_base[i] = value->flags[i];
}
/* Copy over the contours. */
for (i = 0; i < number_of_contours; ++i)
contour_base[i] = (contour_start
+ value->contour_end_points[i]);
/* Establish offsets for anchor points here. It is
possible for glyph anchors to be set to phantom
points, whose coordinates cannot be established until
grid fitting completes. */
if (defer_offsets)
{
x = 0;
y = 0;
/* Assert the child anchor is within the confines of
the zone. */
assert (point2 < value->num_points);
/* Get the points and use them to compute the
offsets. */
xtemp = context->x_coordinates[point];
ytemp = context->y_coordinates[point];
x = (xtemp - value->x_points[point2]);
y = (ytemp - value->y_points[point2]);
}
/* If USE_MY_METRICS is present in this component, save
the instructed phantom points inside CONTEXT.
N.B. such points replace even the unrounded points
within the context, as this distinction is lost in
phantom points sourced from instructed glyphs. */
if (component->flags & 01000) /* USE_MY_METRICS */
{
context->phantom_point_1_x
= context->phantom_point_1_s
= value->x_points[last_point];
context->phantom_point_1_y
= value->y_points[last_point];
context->phantom_point_2_x
= context->phantom_point_2_s
= value->x_points[last_point + 1];
context->phantom_point_2_y
= value->y_points[last_point + 1];
}
xfree (value);
/* Apply the transform to the points, excluding phantom
points within. */
sfnt_transform_f26dot6 (component, x_base, y_base,
last_point, x, y);
}
}
else
{
/* Compound subglyph. Decompose and instruct the glyph
recursively, and then apply the transform.
If USE_MY_METRICS is not set, save the phantom points
presently in CONTEXT, then restore them afterwards. */
pp1x = context->phantom_point_1_x;
pp1y = context->phantom_point_1_y;
pp1s = context->phantom_point_1_s;
pp2x = context->phantom_point_2_x;
pp2y = context->phantom_point_2_y;
pp2s = context->phantom_point_2_s;
error = sfnt_interpret_compound_glyph_1 (subglyph, interpreter,
state,
context, get_glyph,
free_glyph, hmtx, hhea,
maxp, &sub_metrics,
recursion_count + 1,
dcontext);
if (error)
{
if (need_free)
free_glyph (subglyph, dcontext);
return error;
}
if (!(component->flags & 01000)) /* USE_MY_METRICS */
{
context->phantom_point_1_x = pp1x;
context->phantom_point_1_y = pp1y;
context->phantom_point_1_s = pp1s;
context->phantom_point_2_x = pp2x;
context->phantom_point_2_y = pp2y;
context->phantom_point_2_s = pp2s;
}
/* Anchor points for glyphs with instructions must be
computed after grid fitting completes.
As such, the offset is calculated here, using the points
in the loaded child compound glyph. At present, CONTEXT
incorporates the two phantom points after the end of the
last component within SUBGLYPH. */
if (defer_offsets)
{
x = 0;
y = 0;
/* Renumber the non renumbered point2 to point into the
decomposed component. */
point2 += contour_start;
/* Next, check that the non-renumbered point being
anchored lies inside the glyph data that was
decomposed. */
if (point2 >= context->num_points)
{
if (need_free)
free_glyph (subglyph, dcontext);
return "Invalid anchor reference point";
}
/* Get the points and use them to compute the
offsets. */
xtemp = context->x_coordinates[point];
ytemp = context->y_coordinates[point];
x = (xtemp - context->x_coordinates[point2]);
y = (ytemp - context->y_coordinates[point2]);
}
/* Subtract the two phantom points from context->num_points.
This behavior is correct, as only the subglyph's phantom
points may be provided as anchor points. */
assert (context->num_points - contour_start >= 2);
context->num_points -= 2;
sfnt_transform_f26dot6 (component,
context->x_coordinates + contour_start,
context->y_coordinates + contour_start,
/* Exclude phantom points from
transformations. */
context->num_points - contour_start,
x, y);
}
/* Finally, free the subglyph. */
if (need_free)
free_glyph (subglyph, dcontext);
}
/* Run the program for the entire compound glyph, if any. CONTEXT
should not contain phantom points by this point, so append the
points for this glyph as a whole. */
/* Grow various arrays to include those points. */
rc = sfnt_expand_compound_glyph_context (context,
/* Number of new contours
required. */
0,
/* Number of new points
required. */
2,
&x_base, &y_base,
&flags_base, &contour_base);
/* Store the phantom points within the compound glyph. */
x_base[0] = context->phantom_point_1_x;
x_base[1] = context->phantom_point_2_x;
y_base[0] = context->phantom_point_1_y;
y_base[1] = context->phantom_point_2_y;
flags_base[0] = SFNT_POINT_PHANTOM;
flags_base[1] = SFNT_POINT_PHANTOM;
if (glyph->compound->instruction_length)
{
interpreter->state = *state;
error = sfnt_interpret_compound_glyph_2 (glyph, interpreter,
context, base_index,
base_contour,
metrics,
context->phantom_point_1_s,
context->phantom_point_2_s);
}
return error;
}
/* Interpret the compound glyph GLYPH using the specified INTERPRETER.
Load each component of the compound glyph GLYPH. CONTEXT should be
a reference to a `struct sfnt_compound_glyph_context' that is on
the stack.
Use glyph metrics specified in the HMTX, HHEA and MAXP tables, and
the unscaled metrics for GLYPH specified in METRICS.
If the component is a simple glyph, scale and instruct it
immediately.
If the component is a compound glyph, then load it recursively
until a predetermined recursion level is exceeded.
Set INTERPRETER's state to STATE prior to instructing each
component.
Load component glyphs using GET_GLYPH, which should return whether
or not FREE_GLYPH should be called with the glyph that was loaded.
Call both functions with DCONTEXT as an argument.
Finally, append the resulting contours, run any compound glyph
program, and return the instructed outline in *VALUE.
Value is NULL upon success, and the type of error upon failure. */
TEST_STATIC const char *
sfnt_interpret_compound_glyph (struct sfnt_glyph *glyph,
struct sfnt_interpreter *interpreter,
struct sfnt_graphics_state *state,
sfnt_get_glyph_proc get_glyph,
sfnt_free_glyph_proc free_glyph,
struct sfnt_hmtx_table *hmtx,
struct sfnt_hhea_table *hhea,
struct sfnt_maxp_table *maxp,
struct sfnt_glyph_metrics *metrics,
void *dcontext,
struct sfnt_instructed_outline **value)
{
struct sfnt_compound_glyph_context context;
const char *error;
struct sfnt_instructed_outline *outline;
size_t outline_size, temp;
/* Set up the glyph decomposition context. */
memset (&context, 0, sizeof context);
/* Now start decomposing the glyph. */
error = sfnt_interpret_compound_glyph_1 (glyph, interpreter,
state, &context,
get_glyph, free_glyph,
hmtx, hhea, maxp,
metrics, 0, dcontext);
/* If an error occurs, free the data in the context and return. */
if (error)
{
xfree (context.x_coordinates);
xfree (context.y_coordinates);
xfree (context.flags);
xfree (context.contour_end_points);
return error;
}
/* Copy the compound glyph data into an instructed outline. */
outline_size = sizeof (*outline);
if (ckd_mul (&temp, context.num_end_points,
sizeof *outline->contour_end_points)
|| ckd_add (&outline_size, outline_size, temp)
|| ckd_mul (&temp, context.num_points, sizeof *outline->x_points * 2)
|| ckd_add (&outline_size, outline_size, temp)
|| ckd_add (&outline_size, outline_size, context.num_points))
{
xfree (context.x_coordinates);
xfree (context.y_coordinates);
xfree (context.flags);
xfree (context.contour_end_points);
return "Glyph exceeds maximum permissible size";
}
/* Allocate the outline. */
outline = xmalloc (outline_size);
outline->num_points = context.num_points;
outline->num_contours = context.num_end_points;
outline->contour_end_points = (size_t *) (outline + 1);
outline->x_points = (sfnt_f26dot6 *) (outline->contour_end_points
+ outline->num_contours);
outline->y_points = outline->x_points + outline->num_points;
outline->flags = (unsigned char *) (outline->y_points
+ outline->num_points);
/* Copy over the contour endpoints, points, and flags. Note that
all arrays in `context' are NULL unless num_contours is
non-zero. */
if (context.num_end_points)
memcpy (outline->contour_end_points, context.contour_end_points,
outline->num_contours * sizeof *outline->contour_end_points);
if (context.num_points)
{
memcpy (outline->x_points, context.x_coordinates,
outline->num_points * sizeof *outline->x_points);
memcpy (outline->y_points, context.y_coordinates,
outline->num_points * sizeof *outline->y_points);
memcpy (outline->flags, context.flags, context.num_points);
}
/* Free the context data. */
xfree (context.x_coordinates);
xfree (context.y_coordinates);
xfree (context.flags);
xfree (context.contour_end_points);
*value = outline;
return NULL;
}
/* Unicode Variation Sequence (UVS) support.
Unicode defines a mechanism by which two-codepoint sequences
comprising a ``base character'' and ``variation selector'' combine
to produce a glyph besides that which is mapped to the ``base
character'' itself.
TrueType stores variation selector sequences inside a special 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 is assigned a ``default UVS table''
recording ranges of ``base characters'' absent special variant
glyphs, and a ``non-default UVS table'', linking ``base
characters'' to their respective variant glyphs.
Unicode variation selectors occupy the range formed between 0xfe00
and 0xfe0f, along with that from 0xe0100 to 0xe01ef, within the
Unicode codespace. When a variation selector is encountered as
text is being examined for display with a particular font, that
font's character mapping table is indexed by it, yielding a default
and non-default UVS table. If the base character (which is
directly behind the variation selector) is subsequently located
within the default UVS table, then the glyph represented by this
union of base character and variation selector is that designated
by the base character within any UCS-4 or BMP character mapping
table in the font. Since this glyph is at variance with that
derived from the base character only when the character set of the
character mapping table otherwise consulted is not UCS-4 or BMP,
the distinction between those two glyphs is largely notional.
Should the nondefault UVS table hold the base character, then the
glyph is conversely that enumerated in said table, whose indexing
is facilitated by sfnt_variation_glyph_for_char. And if the base
character isn't present within either table or the tables for the
variation selector are absent in the first place, then the two
codepoints constituting the sequence are immiscible and therefore
the sequence cannot apply to the font.
The approach taken by Emacs character composition routines is
diametric to the approach illustrated above: in place of searching
for variation glyphs each time a variation selector character is
encountered, these routines ascertain which glyphs are linked to
each base character that they have adjudged subject to variation in
advance. See sfntfont_get_variation_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;
ssize_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)
!= (int) 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 (ckd_mul (&temp, num_ranges, sizeof *uvs->ranges)
|| ckd_add (&size, size, temp))
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;
ssize_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 (ckd_mul (&temp, num_mappings, sizeof *uvs->mappings)
|| ckd_add (&size, size, temp))
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 (ckd_mul (&size, cmap->num_var_selector_records,
sizeof *table_offsets)
|| ckd_mul (&size, size, 2))
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 (ckd_add (&table_offsets[j].offset, cmap->offset,
cmap->records[i].default_uvs_offset))
goto bail;
table_offsets[j++].is_nondefault_table = false;
}
if (cmap->records[i].nondefault_uvs_offset)
{
if (ckd_add (&table_offsets[j].offset, cmap->offset,
cmap->records[i].nondefault_uvs_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;
}
/* Compare *(sfnt_char *) K to the Unicode value range V. */
static int
sfnt_compare_unicode_value_range (const void *k, const void *v)
{
const sfnt_char *key;
const struct sfnt_unicode_value_range *value;
key = k;
value = v;
if (*key < value->start_unicode_value)
return -1;
else if ((*key - value->start_unicode_value
<= value->additional_count))
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;
}
/* Return whether the character C is present in the default UVS
mapping table UVS. */
TEST_STATIC bool
sfnt_is_character_default (struct sfnt_default_uvs_table *uvs,
sfnt_char c)
{
/* UVS->ranges comprises ranges of characters sorted in increasing
order; these ranges cannot overlap. */
return (bsearch (&c, uvs->ranges, uvs->num_unicode_value_ranges,
sizeof *uvs->ranges,
sfnt_compare_unicode_value_range) != NULL);
}
#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 */
/* Glyph variations. Instead of defining separate fonts for each
combination of weight, width and slant (bold, condensed, italic,
etc), some fonts specify a list of ``variation axes'', which are
options that accept values consisting of numbers on scales
governing deltas applied to select points in their glyphs.
Particular styles within the font are then supplied as sets of
values on these scales to which their respective axes are set,
termed ``instances''.
This optional information is specified in the `fvar' (font
variation), `gvar' (glyph variation) and `cvar' (CVT variation)
tables in a font file. */
/* Read an fvar table from the given font FD. Use the table directory
specified in SUBTABLE.
Return the fvar table upon success, else NULL. */
TEST_STATIC struct sfnt_fvar_table *
sfnt_read_fvar_table (int fd, struct sfnt_offset_subtable *subtable)
{
struct sfnt_table_directory *directory;
struct sfnt_fvar_table *fvar;
ssize_t rc;
size_t min_bytes, ps_size, non_ps_size, temp, pad;
off_t offset;
int i, j;
char *buffer;
sfnt_fixed *coords;
/* Find the table in the directory. */
directory = sfnt_find_table (subtable, SFNT_TABLE_FVAR);
if (!directory)
return NULL;
min_bytes = SFNT_ENDOF (struct sfnt_fvar_table,
instance_size, uint16_t);
/* Check that the length is at least min_bytes. */
if (directory->length < min_bytes)
return NULL;
/* Seek to the location given in the directory. */
if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
return NULL;
/* Allocate enough to hold the fvar table header. */
fvar = xmalloc (sizeof *fvar);
/* Read the fvar table header. */
buffer = NULL;
rc = read (fd, fvar, min_bytes);
if (rc == -1 || rc != min_bytes)
goto bail;
/* Swap what was read. */
sfnt_swap16 (&fvar->major_version);
sfnt_swap16 (&fvar->minor_version);
sfnt_swap16 (&fvar->offset_to_data);
sfnt_swap16 (&fvar->count_size_pairs);
sfnt_swap16 (&fvar->axis_count);
sfnt_swap16 (&fvar->axis_size);
sfnt_swap16 (&fvar->instance_count);
sfnt_swap16 (&fvar->instance_size);
/* major_version should be 1, and minor_version 0. */
if (fvar->major_version != 1 || fvar->minor_version)
goto bail;
/* count_size_pairs should be more than 2. */
if (fvar->count_size_pairs < 2)
goto bail;
/* Don't try to read tables where the axis format differs. */
if (fvar->axis_size != 20)
goto bail;
/* The instance size must either be 2 * sizeof (uint16_t) +
axisCount * sizeof (sfnt_fixed), meaning there is no PostScript
name identifier, or 3 * sizeof (uint16_t) + axisCount * sizeof
(sfnt_fixed), meaning there is. */
if (ckd_mul (&temp, fvar->axis_count, sizeof (sfnt_fixed))
|| ckd_add (&non_ps_size, temp, 2 * sizeof (uint16_t)))
goto bail;
if (ckd_mul (&temp, fvar->axis_count, sizeof (sfnt_fixed))
|| ckd_add (&ps_size, temp, 3 * sizeof (uint16_t)))
goto bail;
if (fvar->instance_size != non_ps_size
&& fvar->instance_size != ps_size)
goto bail;
/* Now compute the offset of the axis data from the start of the
font file. */
if (ckd_add (&offset, fvar->offset_to_data, directory->offset))
goto bail;
/* Seek there. */
if (lseek (fd, offset, SEEK_SET) != offset)
goto bail;
min_bytes = sizeof *fvar;
/* Now, read each axis and instance. Calculate how much extra data
needs to be allocated for the axes and instances: this is
fvar->axis_count * sizeof (struct sfnt_variation_axis), some
padding, and finally fvar->instance_count * sizeof (struct
sfnt_instance) + sizeof (sfnt_fixed) * fvar->instance_count *
fvar->axis_count. */
if (ckd_mul (&temp, fvar->axis_count, sizeof *fvar->axis)
|| ckd_add (&min_bytes, min_bytes, temp))
goto bail;
pad = alignof (struct sfnt_instance);
pad -= min_bytes & (pad - 1);
if (ckd_add (&min_bytes, min_bytes, pad))
goto bail;
if (ckd_mul (&temp, fvar->instance_count, sizeof *fvar->instance)
|| ckd_add (&min_bytes, min_bytes, temp))
goto bail;
if (ckd_mul (&temp, fvar->instance_count, sizeof *fvar->instance->coords)
|| ckd_mul (&temp, temp, fvar->axis_count)
|| ckd_add (&min_bytes, min_bytes, temp))
goto bail;
/* Reallocate fvar. */
fvar = xrealloc (fvar, min_bytes);
/* Fill in offsets. */
fvar->axis = (struct sfnt_variation_axis *) (fvar + 1);
fvar->instance
= (struct sfnt_instance *) (((char *) (fvar->axis
+ fvar->axis_count))
+ pad);
/* Read axes. */
if (directory->length - SFNT_ENDOF (struct sfnt_fvar_table,
instance_size, uint16_t)
< sizeof *fvar->axis * fvar->axis_count)
goto bail;
rc = read (fd, fvar->axis, sizeof *fvar->axis * fvar->axis_count);
if (rc == -1 || rc != sizeof *fvar->axis * fvar->axis_count)
goto bail;
/* Swap each axis. */
for (i = 0; i < fvar->axis_count; ++i)
{
sfnt_swap32 (&fvar->axis[i].axis_tag);
sfnt_swap32 (&fvar->axis[i].min_value);
sfnt_swap32 (&fvar->axis[i].default_value);
sfnt_swap32 (&fvar->axis[i].max_value);
sfnt_swap16 (&fvar->axis[i].flags);
sfnt_swap16 (&fvar->axis[i].name_id);
}
/* Read each instance. */
if (fvar->instance_size < 1024 * 16)
buffer = alloca (fvar->instance_size);
else
buffer = xmalloc (fvar->instance_size);
coords = (sfnt_fixed *) (fvar->instance + fvar->instance_count);
for (i = 0; i < fvar->instance_count; ++i)
{
rc = read (fd, buffer, fvar->instance_size);
if (rc != fvar->instance_size)
goto bail;
/* Fill in various fields. */
fvar->instance[i].name_id = *((uint16_t *) buffer);
fvar->instance[i].flags = *((uint16_t *) buffer + 1);
fvar->instance[i].ps_name_id = 0;
sfnt_swap16 (&fvar->instance[i].name_id);
sfnt_swap16 (&fvar->instance[i].flags);
/* Read coordinates. */
fvar->instance[i].coords = coords;
coords += fvar->axis_count;
memcpy (fvar->instance[i].coords, buffer + 4,
sizeof *fvar->instance[i].coords * fvar->axis_count);
/* Swap coordinates. */
for (j = 0; j < fvar->axis_count; ++j)
sfnt_swap32 (&fvar->instance[i].coords[j]);
/* Read the PostScript name ID if necessary. If not, set it to
nil. */
if (fvar->instance_size == ps_size)
{
fvar->instance[i].ps_name_id
= *(uint16_t *) (buffer + 4 + (sizeof *fvar->instance[i].coords
* fvar->axis_count));
sfnt_swap16 (&fvar->instance[i].ps_name_id);
}
}
/* Free the temporary buffer. */
if (buffer && fvar->instance_size >= 1024 * 16)
xfree (buffer);
/* Return the fvar table. */
return fvar;
bail:
if (buffer && fvar->instance_size >= 1024 * 16)
xfree (buffer);
xfree (fvar);
return NULL;
}
/* Read a gvar table from the given font FD. Use the table directory
specified in SUBTABLE.
Return the gvar table upon success, else NULL. */
TEST_STATIC struct sfnt_gvar_table *
sfnt_read_gvar_table (int fd, struct sfnt_offset_subtable *subtable)
{
struct sfnt_table_directory *directory;
struct sfnt_gvar_table *gvar;
ssize_t rc;
size_t min_bytes, off_size, coordinate_size, data_size;
int i;
off_t offset;
/* Find the table in the directory. */
directory = sfnt_find_table (subtable, SFNT_TABLE_GVAR);
if (!directory)
return NULL;
min_bytes = SFNT_ENDOF (struct sfnt_gvar_table,
offset_to_data, uint32_t);
/* Check that the length is at least min_bytes. */
if (directory->length < min_bytes)
return NULL;
/* Seek to the location given in the directory. */
if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
return NULL;
/* Allocate enough to hold the gvar table header. */
gvar = xmalloc (sizeof *gvar);
/* Read the gvar table header. */
rc = read (fd, gvar, min_bytes);
if (rc == -1 || rc != min_bytes)
goto bail;
/* Swap what was read. */
sfnt_swap16 (&gvar->version);
sfnt_swap16 (&gvar->reserved);
sfnt_swap16 (&gvar->axis_count);
sfnt_swap16 (&gvar->shared_coord_count);
sfnt_swap32 (&gvar->offset_to_coord);
sfnt_swap16 (&gvar->glyph_count);
sfnt_swap16 (&gvar->flags);
sfnt_swap32 (&gvar->offset_to_data);
if (gvar->version != 1)
goto bail;
if (gvar->offset_to_data > directory->length)
goto bail;
/* Figure out the size required for the offset array. Note that
there is one extra offset at the end of the array to mark the
size of the last glyph. */
if (gvar->flags & 1)
/* Offsets are long words. */
off_size = sizeof (uint32_t) * (gvar->glyph_count + 1);
else
/* Offsets are words. */
off_size = sizeof (uint16_t) * (gvar->glyph_count + 1);
/* Now figure out the size of the shared coordinates. */
coordinate_size = (gvar->shared_coord_count * gvar->axis_count
* sizeof (uint16_t));
/* And the size of the glyph variation data. */
data_size = directory->length - gvar->offset_to_data;
/* Wraparound. */
if (data_size > directory->length)
goto bail;
/* Figure out how big gvar needs to be. */
if (ckd_add (&min_bytes, coordinate_size, sizeof *gvar)
|| ckd_add (&min_bytes, min_bytes, off_size)
|| ckd_add (&min_bytes, min_bytes, data_size))
goto bail;
/* Now allocate enough for all of this extra data. */
gvar = xrealloc (gvar, min_bytes);
/* Start reading offsets. */
if (!(gvar->flags & 1))
{
gvar->u.offset_word = (uint16_t *) (gvar + 1);
rc = read (fd, gvar->u.offset_word, off_size);
if (rc != off_size)
goto bail;
for (i = 0; i <= gvar->glyph_count; ++i)
sfnt_swap16 (&gvar->u.offset_word[i]);
}
else
{
gvar->u.offset_long = (uint32_t *) (gvar + 1);
rc = read (fd, gvar->u.offset_long, off_size);
if (rc == -1 || rc != off_size)
goto bail;
for (i = 0; i <= gvar->glyph_count; ++i)
sfnt_swap32 (&gvar->u.offset_long[i]);
}
/* Start reading shared coordinates. */
gvar->global_coords = ((sfnt_f2dot14 *) ((char *) (gvar + 1)
+ off_size));
if (gvar->shared_coord_count)
{
if (ckd_add (&offset, gvar->offset_to_coord, directory->offset))
goto bail;
if (lseek (fd, offset, SEEK_SET) != offset)
goto bail;
rc = read (fd, gvar->global_coords, coordinate_size);
if (rc == -1 || rc != coordinate_size)
goto bail;
for (i = 0; i < coordinate_size / sizeof *gvar->global_coords; ++i)
sfnt_swap16 (&gvar->global_coords[i]);
}
/* Finally, read the rest of the glyph variation data. */
gvar->data_size = data_size;
gvar->glyph_variation_data
= (unsigned char *) (gvar->global_coords
+ (coordinate_size
/ sizeof *gvar->global_coords));
if (gvar->data_size)
{
if (ckd_add (&offset, gvar->offset_to_data, directory->offset))
goto bail;
if (lseek (fd, offset, SEEK_SET) != offset)
goto bail;
rc = read (fd, gvar->glyph_variation_data, gvar->data_size);
if (rc == -1 || rc != gvar->data_size)
goto bail;
}
/* Return the read gvar table. */
return gvar;
bail:
xfree (gvar);
return NULL;
}
/* Read an avar table from the given font FD. Use the table directory
specified in SUBTABLE.
Return the avar table upon success, else NULL. */
TEST_STATIC struct sfnt_avar_table *
sfnt_read_avar_table (int fd, struct sfnt_offset_subtable *subtable)
{
struct sfnt_table_directory *directory;
struct sfnt_avar_table *avar;
ssize_t rc;
size_t min_size, size, i, k, j;
uint16_t *buffer;
struct sfnt_short_frac_correspondence *correspondences;
/* Find the table in the directory. */
directory = sfnt_find_table (subtable, SFNT_TABLE_AVAR);
if (!directory)
return NULL;
min_size = SFNT_ENDOF (struct sfnt_avar_table, axis_count, uint32_t);
/* Check that the length is at least min_size. */
if (directory->length < min_size)
return NULL;
/* Seek to the location given in the directory. */
if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
return NULL;
/* Allocate enough to hold the avar table header. */
avar = xmalloc (sizeof *avar);
/* Read the avar table header. */
rc = read (fd, avar, min_size);
if (rc == -1 || rc != min_size)
goto bail;
/* Swap what was read. */
sfnt_swap32 (&avar->version);
sfnt_swap32 (&avar->axis_count);
if (avar->version != 0x00010000)
goto bail;
if (avar->axis_count < 0)
goto bail;
/* Allocate a buffer that holds the rest of the data. */
size = directory->length - min_size;
buffer = xmalloc (size);
rc = read (fd, buffer, size);
if (rc == -1 || rc != size)
goto bail1;
/* Swap each word. */
for (i = 0; i < size / sizeof *buffer; ++i)
sfnt_swap16 (&buffer[i]);
/* Now, determine how big the resulting data needs to be. Each
struct has a pointer field, and that should be its alignment. */
k = 0;
min_size = sizeof *avar;
for (i = 0; i < avar->axis_count; ++i)
{
/* Check that k remains within bounds. */
if (k >= size / sizeof *buffer)
goto bail1;
/* Now add one struct sfnt_short_frac_segment for each axis and
each of its correspondences. */
if (ckd_add (&min_size, min_size, sizeof (struct sfnt_short_frac_segment))
|| ckd_add (&min_size, min_size,
(sizeof (struct sfnt_short_frac_correspondence)
* buffer[k])))
goto bail1;
/* Verify that words from here to buffer[1 + buffer[k] * 2], the
next pairCount field, are within bounds. */
j = k + 1 + buffer[k] * 2;
if (j > size / sizeof *buffer)
goto bail1;
/* Move to the next pairCount field. */
k = j;
}
/* Resize avar to min_size and start filling in various
pointers. */
avar = xrealloc (avar, min_size);
avar->segments = (struct sfnt_short_frac_segment *) (avar + 1);
correspondences
= ((struct sfnt_short_frac_correspondence *) (avar->segments
+ avar->axis_count));
k = 0;
for (i = 0; i < avar->axis_count; ++i)
{
avar->segments[i].pair_count = buffer[k++];
avar->segments[i].correspondence = correspondences;
for (j = 0; j < avar->segments[i].pair_count; ++j)
{
correspondences->from_coord = buffer[k++];
correspondences->to_coord = buffer[k++];
correspondences++;
}
}
/* Return the read avar table. Free buffer. */
xfree (buffer);
return avar;
bail1:
xfree (buffer);
bail:
xfree (avar);
return NULL;
}
/* Read a sequence of packed points starting from DATA. Return the
number of points read in *NPOINTS_RETURN and the array of unpacked
points, or NULL upon failure.
If non-NULL, set LOCATION to DATA plus the number of bytes read
upon success.
Return (uint16_t *) -1 if there are no points at all.
In this case, deltas will apply to all points in the glyph,
and *NPOINTS_RETURN will be UINT16_MAX.
END is one byte past the last byte in DATA. */
static uint16_t *
sfnt_read_packed_points (unsigned char *restrict data,
uint16_t *npoints_return,
unsigned char *restrict end,
unsigned char *restrict *location)
{
int npoints;
uint16_t *points;
int i, first, control;
points = NULL;
npoints = 0;
if (data >= end)
return NULL;
/* Load the control byte. */
control = *data++;
if (!control)
{
*npoints_return = UINT16_MAX;
*location = data;
return (uint16_t *) -1;
}
/* Now figure out the number of points within. */
if (control & 0x80)
{
npoints = control & 0x7f;
npoints <<= 8;
if (data >= end)
return NULL;
npoints |= *data++;
}
else
npoints = control;
/* Start reading points. */
first = 0;
i = 0;
points = xmalloc (sizeof *points * npoints);
while (i < npoints)
{
if (data >= end)
goto bail;
control = *data++;
if (control & 0x80)
{
/* Next control & 0x7f words are points. */
control &= 0x7f;
while (control != -1 && i < npoints)
{
if (data >= end || data + 1 >= end)
goto bail;
first += *data++ << 8u;
first += *data++;
points[i] = first;
control -= 1, ++i;
}
}
else
{
/* Next control bytes are points. */
while (control != -1 && i < npoints)
{
if (data >= end)
goto bail;
first += *data++;
points[i] = first;
control -= 1, ++i;
}
}
}
/* Return the points read. */
*npoints_return = npoints;
*location = data;
return points;
bail:
xfree (points);
return NULL;
}
/* Read and return N packed deltas from DATA. Set *DATA_RETURN to
DATA plus the number of bytes read.
END is the end of the glyph variation data. Value is an array of N
deltas upon success, and NULL upon failure. */
static sfnt_fword *
sfnt_read_packed_deltas (unsigned char *restrict data,
unsigned char *restrict end,
int n,
unsigned char *restrict *data_return)
{
sfnt_fword *deltas;
int i, count;
unsigned char control;
uint16_t value;
if (data >= end)
return NULL;
deltas = xmalloc (sizeof *deltas * n);
i = 0;
while (i < n)
{
if (data >= end)
goto fail;
control = *data++;
count = control & 0x3f;
while (count != -1 && i < n)
{
if (control & 0x80)
deltas[i++] = 0;
else if (control & 0x40)
{
if (data + 1 >= end)
goto fail;
value = *data++ << 8;
value |= *data++;
deltas[i++] = value;
}
else
{
if (data >= end)
goto fail;
deltas[i++] = (signed char) *data++;
}
--count;
}
}
*data_return = data;
return deltas;
fail:
xfree (deltas);
return NULL;
}
/* Read a cvar table from the given font FD. Use the table directory
specified in SUBTABLE, axis information provided in the fvar table
FVAR, and CVT information provided in the cvt table CVT.
Return the cvar table upon success, else NULL. */
TEST_STATIC struct sfnt_cvar_table *
sfnt_read_cvar_table (int fd, struct sfnt_offset_subtable *subtable,
struct sfnt_fvar_table *fvar,
struct sfnt_cvt_table *cvt)
{
struct sfnt_table_directory *directory;
struct sfnt_cvar_table *cvar;
ssize_t rc;
size_t ntuples, size;
int i, j;
sfnt_f2dot14 *coords;
uint16_t *local, *points, npoints, data_size, min_size, index;
unsigned char *buffer, *data, *end, *tuple;
ptrdiff_t data_offset;
sfnt_fword *deltas;
/* Find the table in the directory. */
directory = sfnt_find_table (subtable, SFNT_TABLE_CVAR);
if (!directory)
return NULL;
min_size = SFNT_ENDOF (struct sfnt_cvar_table, data_offset,
uint16_t);
/* Check that the length is at least min_size. */
if (directory->length < min_size)
return NULL;
/* Seek to the location given in the directory. */
if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
return NULL;
/* Allocate enough to hold the cvar table header. */
cvar = xmalloc (sizeof *cvar);
/* Read the cvar table header. */
rc = read (fd, cvar, min_size);
if (rc != min_size)
goto bail;
/* Swap what was read. */
sfnt_swap32 (&cvar->version);
sfnt_swap16 (&cvar->tuple_count);
sfnt_swap16 (&cvar->data_offset);
/* Read the rest of the table. */
size = directory->length - min_size;
buffer = xmalloc (size);
rc = read (fd, buffer, size);
if (rc == -1 || rc != size)
goto bail;
/* Now figure out how large cvar must be by reading the tuples. */
ntuples = cvar->tuple_count & 0x0fff;
data_offset = ((ptrdiff_t) cvar->data_offset
- (ptrdiff_t) min_size);
end = buffer + size;
if (data_offset < 0)
goto bail1;
/* See if there are shared points, and read them if there are. */
data = buffer + data_offset;
tuple = buffer;
points = NULL;
/* Initialize `npoints' to zero. The specification doesn't say what
should happen with tuples using shared point numbers if it is not
set later on; simply assume no points at all apply to such a
tuple. */
npoints = 0;
/* Initialize `size' to 0. */
size = 0;
if (cvar->tuple_count & 0x8000)
{
points = sfnt_read_packed_points (data, &npoints, end,
&tuple);
if (!points)
goto bail1;
/* Add npoints words to the size. If npoints is UINT16_MAX, no
coordinates will actually be allocated. */
if (npoints != UINT16_MAX)
size = npoints * sizeof *points;
}
while (ntuples--)
{
data = buffer + data_offset;
/* Read the tuple. */
if (tuple + 3 >= end)
goto bail2;
memcpy (&data_size, tuple, sizeof data_size);
tuple += sizeof data_size;
memcpy (&index, tuple, sizeof index);
tuple += sizeof index;
sfnt_swap16 (&data_size);
sfnt_swap16 (&index);
/* Increment the offset to the data by the data size specified
here. */
data_offset += data_size;
if (index & 0x8000)
{
/* Embedded coordinates are present. Read each coordinate
and add it to the size. */
if (tuple + fvar->axis_count * sizeof *coords - 1 >= end)
goto bail2;
tuple += sizeof *coords * fvar->axis_count;
if (ckd_add (&size, size, sizeof *coords * fvar->axis_count))
goto bail2;
}
else
/* This table is invalid, as cvar tables don't have global
coordinates. */
goto bail2;
/* Now read indeterminate tuples if required. */
if (index & 0x4000)
{
tuple += fvar->axis_count * 4;
if (ckd_add (&size, size, fvar->axis_count * 4))
goto bail2;
}
/* Add one point and one delta for each CVT element. */
if (ckd_add (&size, size, cvt->num_elements * 4))
goto bail2;
/* Now add the size of the tuple. */
if (ckd_add (&size, size, sizeof *cvar->variation))
goto bail2;
}
if (ckd_add (&size, size, sizeof *cvar))
goto bail2;
/* Reallocate cvar. */
cvar = xrealloc (cvar, size);
ntuples = cvar->tuple_count & 0x0fff;
cvar->variation = (struct sfnt_tuple_variation *) (cvar + 1);
coords = (sfnt_f2dot14 *) (cvar->variation + ntuples);
tuple = buffer;
data_offset = ((ptrdiff_t) cvar->data_offset
- (ptrdiff_t) min_size);
/* Start reading the tuples into cvar. */
for (i = 0; i < ntuples; ++i)
{
data = buffer + data_offset;
/* Read the tuple. */
if (tuple + 3 >= end)
goto bail2;
memcpy (&data_size, tuple, sizeof data_size);
tuple += sizeof data_size;
memcpy (&index, tuple, sizeof index);
tuple += sizeof index;
sfnt_swap16 (&data_size);
sfnt_swap16 (&index);
/* Increment the offset to the data by the data size specified
here. */
data_offset += data_size;
cvar->variation[i].intermediate_start = NULL;
cvar->variation[i].intermediate_end = NULL;
if (index & 0x8000)
{
/* Embedded coordinates are present. Read each
coordinate. */
cvar->variation[i].coordinates = coords;
for (j = 0; j < fvar->axis_count; ++j)
{
if (tuple + 1 >= end)
goto bail2;
memcpy (coords++, tuple, sizeof *coords);
tuple += sizeof *coords;
sfnt_swap16 (coords);
}
}
else
goto bail2;
/* Now read indeterminate tuples if required. */
if (index & 0x4000)
{
cvar->variation[i].intermediate_start = coords;
for (j = 0; j < fvar->axis_count; ++j)
{
if (tuple + 1 >= end)
goto bail2;
memcpy (coords++, tuple, sizeof *coords);
tuple += sizeof *coords;
sfnt_swap16 (coords);
}
cvar->variation[i].intermediate_end = coords;
for (j = 0; j < fvar->axis_count; ++j)
{
if (tuple + 1 >= end)
goto bail2;
memcpy (coords++, tuple, sizeof *coords);
tuple += sizeof *coords;
sfnt_swap16 (coords);
}
}
/* Finally, read private ``point'' numbers. If this flag is not
set, use shared point numbers previously read.
Read at most CVT->num_elements points, as that is all the
storage allocated. */
if (index & 0x2000)
{
local = sfnt_read_packed_points (data, &cvar->variation[i].num_points,
end, &data);
if (!local)
goto bail2;
/* If points apply to all CVT indices, skip this part. */
if (cvar->variation[i].num_points != UINT16_MAX)
{
if (cvar->variation[i].num_points > cvt->num_elements)
cvar->variation[i].num_points = cvt->num_elements;
cvar->variation[i].points = (uint16_t *) coords;
for (j = 0; j < cvar->variation[i].num_points; ++j)
*coords++ = local[j];
xfree (local);
}
else
cvar->variation[i].points = NULL;
}
else
{
/* Copy in the shared point numbers instead. */
cvar->variation[i].num_points = npoints;
if (npoints != UINT16_MAX)
{
if (cvar->variation[i].num_points > cvt->num_elements)
cvar->variation[i].num_points = cvt->num_elements;
cvar->variation[i].points = (uint16_t *) coords;
for (j = 0; j < cvar->variation[i].num_points; ++j)
*coords++ = points[j];
}
else
cvar->variation[i].points = NULL;
}
/* And read packed deltas. If cvar->variation[i].num_points is
UINT16_MAX, then there is one delta for each CVT entry.
Otherwise, there are that many deltas. */
if (cvar->variation[i].num_points == UINT16_MAX)
{
deltas = sfnt_read_packed_deltas (data, end, cvt->num_elements,
&data);
if (!deltas)
goto bail2;
cvar->variation[i].deltas = coords;
for (j = 0; j < cvt->num_elements; ++j)
*coords++ = deltas[j];
xfree (deltas);
}
else
{
deltas = sfnt_read_packed_deltas (data, end,
cvar->variation[i].num_points,
&data);
if (!deltas)
goto bail2;
cvar->variation[i].deltas = coords;
for (j = 0; j < cvar->variation[i].num_points; ++j)
*coords++ = deltas[j];
xfree (deltas);
}
}
/* Free data and return the read cvar table. */
if (points != (void *) -1)
xfree (points);
xfree (buffer);
return cvar;
bail2:
if (points != (void *) -1)
xfree (points);
bail1:
xfree (buffer);
bail:
xfree (cvar);
return NULL;
}
/* Initialize the specified BLEND with the given FVAR and GVAR tables.
If non-NULL, adjust normalized coordinates using the axis variation
table AVAR; similarly, adjust interpreter CVT values using CVAR, if
specified. */
TEST_STATIC void
sfnt_init_blend (struct sfnt_blend *blend, struct sfnt_fvar_table *fvar,
struct sfnt_gvar_table *gvar, struct sfnt_avar_table *avar,
struct sfnt_cvar_table *cvar)
{
size_t size;
blend->fvar = fvar;
blend->gvar = gvar;
blend->avar = avar;
blend->cvar = cvar;
/* Allocate a single array to hold both coords and norm_coords. */
size = (fvar->axis_count * sizeof *blend->coords * 2);
blend->coords = xmalloc (size);
blend->norm_coords = blend->coords + fvar->axis_count;
}
/* Free what was initialized in the specified BLEND. */
TEST_STATIC void
sfnt_free_blend (struct sfnt_blend *blend)
{
xfree (blend->coords);
}
/* Normalize BLEND->fvar->axis_count coordinates in BLEND->coords and
place the result in BLEND->norm_coords. */
TEST_STATIC void
sfnt_normalize_blend (struct sfnt_blend *blend)
{
struct sfnt_variation_axis *axis;
int i, j;
sfnt_fixed from, coord, j0, j1, j2;
sfnt_fixed from_last, coord_last;
struct sfnt_short_frac_segment *segment;
/* For each axis... */
for (i = 0; i < blend->fvar->axis_count; ++i)
{
/* Normalize based on [min, default, max], into [-1, 0, 1]. */
axis = &blend->fvar->axis[i];
/* Load the current design coordinate. */
coord = blend->coords[i];
/* Keep it within bounds. */
if (coord > axis->max_value)
coord = axis->max_value;
else if (coord < axis->min_value)
coord = axis->min_value;
if (coord > axis->default_value)
{
/* Avoid division by 0. */
if (axis->max_value != axis->default_value)
blend->norm_coords[i]
= sfnt_div_fixed (sfnt_sub (coord, axis->default_value),
sfnt_sub (axis->max_value,
axis->default_value));
else
blend->norm_coords[i] = 0;
}
else if (coord < axis->default_value)
{
if (axis->default_value != axis->min_value)
blend->norm_coords[i]
= sfnt_div_fixed (sfnt_sub (coord, axis->default_value),
sfnt_sub (axis->default_value,
axis->min_value));
else
blend->norm_coords[i] = 0;
}
else
blend->norm_coords[i] = 0;
}
/* Now, apply axis variations, but only if the avar table has the
right number of axes. */
if (blend->avar && (blend->fvar->axis_count
== blend->avar->axis_count))
{
for (i = 0; i < blend->fvar->axis_count; ++i)
{
segment = &blend->avar->segments[i];
/* Search for a correspondence record above the normalized
coordinate of this axis. */
for (j = 1; j < segment->pair_count; ++j)
{
from = segment->correspondence[j].from_coord * 4;
coord = segment->correspondence[j].to_coord * 4;
if (blend->norm_coords[i] < from)
{
from_last
= segment->correspondence[j - 1].from_coord * 4;
coord_last
= segment->correspondence[j - 1].to_coord * 4;
j0 = blend->norm_coords[i] - from_last;
j1 = coord - coord_last;
j2 = from - from_last;
blend->norm_coords[i]
= (sfnt_multiply_divide_signed (j0, j1, j2) + coord_last);
break;
}
}
}
}
}
struct sfnt_gvar_glyph_header
{
/* A packed field. The high 4 bits are flags and the low 12 bits are
the number of tuples for this glyph. The number of tuples can be
any number between 1 and 4095. */
uint16_t tuple_count;
/* Offset from the start of the GlyphVariationData table to the
serialized data. */
uint16_t data_offset;
};
/* Given a BLEND containing normalized coordinates, an array of
BLEND->gvar->axis_count tuple coordinates, and, if INTERMEDIATE_P,
a range of tuple coordinates from INTERMEDIATE_START to
INTERMEDIATE_END, return the scaling factor to apply to deltas for
each corresponding point. */
static sfnt_fixed
sfnt_compute_tuple_scale (struct sfnt_blend *blend, bool intermediate_p,
sfnt_f2dot14 *coords,
sfnt_f2dot14 *intermediate_start,
sfnt_f2dot14 *intermediate_end)
{
int i;
sfnt_fixed coord, start UNINIT, end UNINIT;
sfnt_fixed scale;
/* scale is initially 1.0. */
scale = 0200000;
for (i = 0; i < blend->gvar->axis_count; ++i)
{
/* Load values for this axis, scaled up to sfnt_fixed. */
coord = coords[i] * 4;
/* GCC warns about start and end being used when uninitialized,
but they are used only if intermediate_p. */
if (intermediate_p)
{
start = intermediate_start[i] * 4;
end = intermediate_end[i] * 4;
}
/* Ignore tuples that can be skipped. */
if (!coord)
continue;
/* If the coordinate is set to 0, then deltas should not be
applied. Return 0. */
if (!blend->norm_coords[i])
return 0;
/* If no scaling need take place, continue. */
if (blend->norm_coords[i] == coord)
continue;
if (!intermediate_p)
{
/* Not an intermediate tuple; if coord is less than 0 and
blend->norm_coords[i] < coord, or coord is more than 0
and blend->norm_coords[i] > coord, then it doesn't fit,
so return. */
if (blend->norm_coords[i] < MIN (0, coord)
|| blend->norm_coords[i] > MAX (0, coord))
return 0;
scale = sfnt_multiply_divide_signed (scale,
blend->norm_coords[i],
coord);
}
else
{
/* Otherwise, renormalize between start and end. */
if (blend->norm_coords[i] < start
|| blend->norm_coords[i] > end)
return 0;
if (blend->norm_coords[i] < coord)
scale = sfnt_multiply_divide (scale,
blend->norm_coords[i] - start,
coord - start);
else
scale = sfnt_multiply_divide (scale,
end - blend->norm_coords[i],
end - coord);
}
}
return scale;
}
/* Move each point in the simple glyph GLYPH between PAIR_START and
PAIR_END to agree with the positions of those two anchor points as
compared with their initial positions recorded within the arrays X
and Y.
The range formed between PAIR_START and PAIR_END may encompass the
upper extreme of the contour between START and END. */
static void
sfnt_infer_deltas_2 (struct sfnt_glyph *glyph, size_t pair_start,
size_t pair_end, size_t start, size_t end,
sfnt_fword *x, sfnt_fword *y)
{
size_t j;
sfnt_fword min_pos, max_pos, position, d1, d2;
sfnt_fixed ratio, delta;
j = pair_start + 1;
while (j != pair_end)
{
/* Reset j to the contour's start position if it is about to
overrun this contour. */
if (j > end)
{
/* The start of the contour might also be the end of this
reference point. */
if (start == pair_end)
return;
j = start;
}
/* Consider the X axis. Set min_pos and max_pos to the
smallest and greatest values along that axis. */
min_pos = MIN (x[pair_start], x[pair_end]);
max_pos = MAX (x[pair_start], x[pair_end]);
/* Now see if the current point lies between min and
max...
GX interpolation differs from IUP in one important detail:
points are shifted to follow the movement of their reference
points if their positions are identical to those of any of
their reference points, whereas IUP considers such points to
fall within their reference points. */
if (x[j] > min_pos && x[j] < max_pos)
{
/* Interpolate between min_pos and max_pos. */
ratio = sfnt_div_fixed ((sfnt_sub (x[j], min_pos)
* 65536),
(sfnt_sub (max_pos, min_pos)
* 65536));
/* Load the current positions of pair_start and pair_end
along this axis. */
min_pos = MIN (glyph->simple->x_coordinates[pair_start],
glyph->simple->x_coordinates[pair_end]);
max_pos = MAX (glyph->simple->x_coordinates[pair_start],
glyph->simple->x_coordinates[pair_end]);
/* Lerp in between. */
delta = sfnt_sub (max_pos, min_pos);
delta = sfnt_mul_fixed (ratio, delta);
glyph->simple->x_coordinates[j] = min_pos + delta;
}
else
{
/* ... otherwise, move point j by the delta of the
nearest touched point. */
/* If min_pos and max_pos are the same, apply
pair_start's delta if it is identical to that of
pair_end, or apply nothing at all otherwise. */
if (min_pos == max_pos)
{
d1 = (glyph->simple->x_coordinates[pair_start]
- x[pair_start]);
d2 = (glyph->simple->x_coordinates[pair_end]
- x[pair_end]);
if (d1 == d2)
glyph->simple->x_coordinates[j] += d1;
goto consider_y;
}
if (x[j] >= max_pos)
{
position = MAX (glyph->simple->x_coordinates[pair_start],
glyph->simple->x_coordinates[pair_end]);
delta = position - max_pos;
}
else
{
position = MIN (glyph->simple->x_coordinates[pair_start],
glyph->simple->x_coordinates[pair_end]);
delta = position - min_pos;
}
glyph->simple->x_coordinates[j] = x[j] + delta;
}
consider_y:
/* Now, consider the Y axis. */
min_pos = MIN (y[pair_start], y[pair_end]);
max_pos = MAX (y[pair_start], y[pair_end]);
/* Now see if the current point lies between min and
max...
GX interpolation differs from IUP in one important detail:
points are shifted to follow the movement of their reference
points if their positions are identical to those of any of
their reference points, whereas IUP considers such points to
fall within their reference points. */
if (y[j] > min_pos && y[j] < max_pos)
{
/* Interpolate between min_pos and max_pos. */
ratio = sfnt_div_fixed ((sfnt_sub (y[j], min_pos)
* 65536),
(sfnt_sub (max_pos, min_pos)
* 65536));
/* Load the current positions of pair_start and pair_end
along this axis. */
min_pos = MIN (glyph->simple->y_coordinates[pair_start],
glyph->simple->y_coordinates[pair_end]);
max_pos = MAX (glyph->simple->y_coordinates[pair_start],
glyph->simple->y_coordinates[pair_end]);
/* Lerp in between. */
delta = sfnt_sub (max_pos, min_pos);
delta = sfnt_mul_fixed (ratio, delta);
glyph->simple->y_coordinates[j] = min_pos + delta;
}
else
{
/* ... otherwise, move point j by the delta of the
nearest touched point. */
/* If min_pos and max_pos are the same, apply
pair_start's delta if it is identical to that of
pair_end, or apply nothing at all otherwise. */
if (min_pos == max_pos)
{
d1 = (glyph->simple->y_coordinates[pair_start]
- y[pair_start]);
d2 = (glyph->simple->y_coordinates[pair_end]
- y[pair_end]);
if (d1 == d2)
glyph->simple->y_coordinates[j] += d1;
goto next;
}
if (y[j] >= max_pos)
{
position = MAX (glyph->simple->y_coordinates[pair_start],
glyph->simple->y_coordinates[pair_end]);
delta = position - max_pos;
}
else
{
position = MIN (glyph->simple->y_coordinates[pair_start],
glyph->simple->y_coordinates[pair_end]);
delta = position - min_pos;
}
glyph->simple->y_coordinates[j] = y[j] + delta;
}
next:
j++;
}
}
/* Infer point positions for points that have been partially moved
within the contour in GLYPH denoted by START and END. */
static void
sfnt_infer_deltas_1 (struct sfnt_glyph *glyph, size_t start,
size_t end, bool *touched, sfnt_fword *x,
sfnt_fword *y)
{
size_t i, pair_start, pair_end, pair_first;
pair_start = pair_first = -1;
/* Look for pairs of touched points. */
for (i = start; i <= end; ++i)
{
if (!touched[i])
continue;
if (pair_start == -1)
{
pair_first = i;
goto next;
}
pair_end = i;
/* pair_start to pair_end are now a pair of points whose
intermediates should be interpolated. */
sfnt_infer_deltas_2 (glyph, pair_start, pair_end,
start, end, x, y);
next:
pair_start = i;
}
/* If pair_start is set, then lerp points between it and
pair_first. */
if (pair_start != (size_t) -1)
{
pair_end = pair_first;
/* pair_start to pair_end are now a pair of points whose
intermediates should be interpolated. */
sfnt_infer_deltas_2 (glyph, pair_start, pair_end,
start, end, x, y);
}
}
/* Infer point positions for contours that have been partially moved
by variation. For each contour in GLYPH, find pairs of points
which have had deltas applied. For each such pair, interpolate
points between the first point in the pair and the second by
considering each point along every one of the two axes (X and Y)
like so:
- For each point that lies between the first point and the last
on the axis currently being considered, interpolate its
position in that axis so that the ratio formed by its position
relative to the first and last points of the pair in the
original outline still holds.
- For each point that lies to the left or top of the first point
on the axis being considered, use the delta of the first point.
- And finally, for each point that lies to the right or bottom of
the last point on that axis, use the delta of the last
point.
X and Y contain the original positions of each point.
TOUCHED contains whether or not each point within GLYPH has been
changed through variation.
Apply the inferred deltas back to GLYPH. */
static void
sfnt_infer_deltas (struct sfnt_glyph *glyph, bool *touched,
sfnt_fword *x, sfnt_fword *y)
{
size_t i;
int point, first, end;
point = 0;
for (i = 0; i < glyph->number_of_contours; ++i)
{
first = point;
end = glyph->simple->end_pts_of_contours[i];
/* Return if the glyph is invalid. */
if (first >= glyph->simple->number_of_points
|| end >= glyph->simple->number_of_points
|| first > end)
return;
sfnt_infer_deltas_1 (glyph, first, end, touched, x, y);
point = end + 1;
}
}
/* Read the glyph variation data for the specified glyph ID from
BLEND's gvar table. Apply the offsets to each point in the
specified simple GLYPH, based on the specified BLEND.
Value is 0 upon success, else 1.
The glyph variation data consists of a number of elements, each of
which has its own associated point numbers and deltas, and a list
of one or two coordinates for each axis. Each such list is
referred to as a ``tuple''.
The deltas, one for each point, are multiplied by the normalized
value of each axis and applied to those points for each tuple that
is found to be applicable.
Each element of the glyph variation data is applicable to an axis
if its list of coordinates:
- contains one element for each axis, and its axis has a value
between 0 and that element.
- contains two elements for each axis, and its axis has a value
between the first element and the second.
Return the deltas that would normally be applied to the two phantom
points describing horizontal bounds in *DISTORTION. Do not
transform the outline to reflect adjustments to the origin
point. */
TEST_STATIC int
sfnt_vary_simple_glyph (struct sfnt_blend *blend, sfnt_glyph id,
struct sfnt_glyph *glyph,
struct sfnt_metrics_distortion *distortion)
{
uint32_t offset;
struct sfnt_gvar_glyph_header header;
uint16_t *points, npoints;
int i, ntuples, j, point_count;
unsigned char *tuple, *end, *data;
uint16_t data_size, index, *glyph_points;
sfnt_f2dot14 *restrict coords;
sfnt_f2dot14 *restrict intermediate_start;
sfnt_f2dot14 *restrict intermediate_end;
sfnt_fword *restrict dx, *restrict dy, fword;
struct sfnt_gvar_table *gvar;
uint16_t *local_points, n_local_points;
sfnt_fixed scale;
ptrdiff_t data_offset;
bool *touched;
sfnt_fword *restrict original_x, *restrict original_y;
gvar = blend->gvar;
if (gvar->axis_count != blend->fvar->axis_count)
return 1;
if (gvar->glyph_count <= id)
return 1;
if (gvar->flags & 1)
offset = gvar->u.offset_long[id];
else
offset = gvar->u.offset_word[id] * 2u;
if (offset >= gvar->data_size)
return 1;
end = gvar->glyph_variation_data + gvar->data_size;
/* Start reading the header. */
if (offset + sizeof header > gvar->data_size)
return 1;
/* Clear the distortion. */
distortion->origin = 0;
distortion->advance = 0;
memcpy (&header, gvar->glyph_variation_data + offset,
sizeof header);
/* Swap the header. */
sfnt_swap16 (&header.tuple_count);
sfnt_swap16 (&header.data_offset);
/* Prepare to read each tuple. */
ntuples = header.tuple_count & 0x0fff;
/* Initialize the data offset. This is incremented with each tuple
read. */
data_offset = header.data_offset;
/* If gvar->flags & tuples_share_point_numbers, read the shared
point numbers. Initialize `npoints' to zero. The specification
doesn't say what should happen with tuples using shared point
numbers if it is not set later on; simply assume no points at all
apply to such a tuple. */
npoints = 0;
if (header.tuple_count & 0x8000)
{
data = gvar->glyph_variation_data + offset + data_offset;
points = sfnt_read_packed_points (data, &npoints, end,
&tuple);
if (!points)
return 1;
/* Shared point numbers are part of the data after the tuple
array. Thus, increment data_offset by tuple - data. `tuple'
here holds no relation to a pointer to the current part of
the tuple array that is being read later on. */
data_offset += tuple - data;
}
else
points = NULL;
/* Start reading each tuple. */
tuple = gvar->glyph_variation_data + offset + sizeof header;
if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16)
coords = xmalloc (gvar->axis_count * sizeof *coords * 3);
else
coords = alloca (gvar->axis_count * sizeof *coords * 3);
intermediate_start = coords + gvar->axis_count;
intermediate_end = intermediate_start + gvar->axis_count;
/* Allocate arrays of booleans and fwords to keep track of which
points have been touched. */
touched = NULL;
original_x = NULL;
original_y = NULL;
while (ntuples--)
{
data = gvar->glyph_variation_data + offset + data_offset;
if (tuple + 3 >= end)
goto fail1;
memcpy (&data_size, tuple, sizeof data_size);
tuple += sizeof data_size;
memcpy (&index, tuple, sizeof index);
tuple += sizeof index;
sfnt_swap16 (&data_size);
sfnt_swap16 (&index);
/* Increment the offset to the data by the data size specified
here. */
data_offset += data_size;
if (index & 0x8000)
{
/* Embedded coordinates are present. Read each
coordinate and add it to the tuple. */
for (j = 0; j < gvar->axis_count; ++j)
{
if (tuple + 1 >= end)
goto fail1;
memcpy (&coords[j], tuple, sizeof *coords);
tuple += sizeof *coords;
sfnt_swap16 (&coords[j]);
}
}
else if ((index & 0xfff) > gvar->shared_coord_count)
/* index exceeds the number of shared tuples present. */
goto fail1;
else
/* index points into gvar->axis_count coordinates making up
the tuple. */
memcpy (coords, (gvar->global_coords
+ ((index & 0xfff) * gvar->axis_count)),
gvar->axis_count * sizeof *coords);
/* Now read indeterminate tuples if required. */
if (index & 0x4000)
{
for (j = 0; j < gvar->axis_count; ++j)
{
if (tuple + 1 >= end)
goto fail1;
memcpy (&intermediate_start[j], tuple,
sizeof *intermediate_start);
tuple += sizeof *intermediate_start;
sfnt_swap16 (&intermediate_start[j]);
}
for (j = 0; j < gvar->axis_count; ++j)
{
if (tuple + 1 >= end)
goto fail1;
memcpy (&intermediate_end[j], tuple,
sizeof *intermediate_end);
tuple += sizeof *intermediate_end;
sfnt_swap16 (&intermediate_end[j]);
}
}
/* See whether or not the tuple applies to the current variation
configuration, and how much to scale them by. */
scale = sfnt_compute_tuple_scale (blend, index & 0x4000,
coords, intermediate_start,
intermediate_end);
if (!scale)
continue;
local_points = NULL;
/* Finally, read private point numbers.
Set local_points to those numbers; it will be freed
once the loop ends. */
if (index & 0x2000)
{
local_points = sfnt_read_packed_points (data, &n_local_points,
end, &data);
if (!local_points)
goto fail1;
point_count = n_local_points;
glyph_points = local_points;
}
else
{
/* If there are no private point numbers, use global
points. */
point_count = npoints;
glyph_points = points;
}
/* Now, read packed deltas. */
dx = NULL;
dy = NULL;
switch (point_count)
{
case UINT16_MAX:
/* Deltas are provided for all points in the glyph.
No glyph should have more than 65535 points. */
/* Add 4 phantom points to each end. */
dx = sfnt_read_packed_deltas (data, end,
glyph->simple->number_of_points + 4,
&data);
dy = sfnt_read_packed_deltas (data, end,
glyph->simple->number_of_points + 4,
&data);
if (!dx || !dy)
goto fail3;
/* Apply each delta to the simple glyph. */
for (i = 0; i < glyph->simple->number_of_points; ++i)
{
fword = sfnt_mul_fixed_round (dx[i], scale);
glyph->simple->x_coordinates[i] += fword;
fword = sfnt_mul_fixed_round (dy[i], scale);
glyph->simple->y_coordinates[i] += fword;
}
/* Apply the deltas for the two phantom points. */
distortion->origin += sfnt_mul_fixed_round (dx[i++], scale);
distortion->advance += sfnt_mul_fixed_round (dx[i], scale);
break;
default:
dx = sfnt_read_packed_deltas (data, end, point_count, &data);
dy = sfnt_read_packed_deltas (data, end, point_count, &data);
if (!dx || !dy)
goto fail3;
/* Deltas are only applied for each point number read. */
if (!original_x)
{
if ((glyph->simple->number_of_points
* sizeof *touched) >= 1024 * 16)
touched = xmalloc (sizeof *touched
* glyph->simple->number_of_points);
else
touched = alloca (sizeof *touched
* glyph->simple->number_of_points);
if ((sizeof *original_x * 2
* glyph->simple->number_of_points) >= 1024 * 16)
original_x = xmalloc (sizeof *original_x * 2
* glyph->simple->number_of_points);
else
original_x = alloca (sizeof *original_x * 2
* glyph->simple->number_of_points);
original_y = original_x + glyph->simple->number_of_points;
}
/* The array of original coordinates should reflect the
state of the glyph immediately before deltas from this
tuple are applied, in contrast to the state before any
deltas are applied. */
memcpy (original_x, glyph->simple->x_coordinates,
(sizeof *original_x
* glyph->simple->number_of_points));
memcpy (original_y, glyph->simple->y_coordinates,
(sizeof *original_y
* glyph->simple->number_of_points));
memset (touched, 0, (sizeof *touched
* glyph->simple->number_of_points));
for (i = 0; i < point_count; ++i)
{
/* Apply deltas to phantom points. */
if (glyph_points[i] == glyph->simple->number_of_points)
{
distortion->origin += sfnt_mul_fixed_round (dx[i], scale);
continue;
}
if (glyph_points[i] == glyph->simple->number_of_points + 1)
{
distortion->advance += sfnt_mul_fixed_round (dx[i], scale);
continue;
}
/* Make sure the point doesn't end up out of bounds. */
if (glyph_points[i] >= glyph->simple->number_of_points)
continue;
fword = sfnt_mul_fixed_round (dx[i], scale);
glyph->simple->x_coordinates[glyph_points[i]] += fword;
fword = sfnt_mul_fixed_round (dy[i], scale);
glyph->simple->y_coordinates[glyph_points[i]] += fword;
touched[glyph_points[i]] = true;
}
sfnt_infer_deltas (glyph, touched, original_x,
original_y);
break;
}
xfree (dx);
xfree (dy);
if (local_points != (uint16_t *) -1)
xfree (local_points);
}
/* Return success. */
if ((glyph->simple->number_of_points
* sizeof *touched) >= 1024 * 16)
xfree (touched);
if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16)
xfree (coords);
if ((sizeof *original_x * 2
* glyph->simple->number_of_points) >= 1024 * 16)
xfree (original_x);
if (points != (uint16_t *) -1)
xfree (points);
/* Set the glyph metrics distortion as well. */
glyph->advance_distortion = distortion->advance;
glyph->origin_distortion = distortion->origin;
return 0;
fail3:
xfree (dx);
xfree (dy);
xfree (local_points);
fail1:
if ((glyph->simple->number_of_points
* sizeof *touched) >= 1024 * 16)
xfree (touched);
if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16)
xfree (coords);
if ((sizeof *original_x * 2
* glyph->simple->number_of_points) >= 1024 * 16)
xfree (original_x);
if (points != (uint16_t *) -1)
xfree (points);
return 1;
}
/* Read the glyph variation data for the specified glyph ID from
BLEND's gvar table. Apply the deltas specified within to each
component with offsets in the specified compound GLYPH, based on
the specified BLEND. Return distortions to phantom points in
*DISTORTION.
Value is 0 upon success, 1 otherwise. */
TEST_STATIC int
sfnt_vary_compound_glyph (struct sfnt_blend *blend, sfnt_glyph id,
struct sfnt_glyph *glyph,
struct sfnt_metrics_distortion *distortion)
{
uint32_t offset;
struct sfnt_gvar_glyph_header header;
uint16_t *points, npoints;
int i, ntuples, j, point_count;
unsigned char *tuple, *end, *data;
uint16_t data_size, index, *glyph_points;
sfnt_f2dot14 *restrict coords;
sfnt_f2dot14 *restrict intermediate_start;
sfnt_f2dot14 *restrict intermediate_end;
sfnt_fword *restrict dx, *restrict dy, fword, word;
struct sfnt_gvar_table *gvar;
uint16_t *local_points, n_local_points;
sfnt_fixed scale;
ptrdiff_t data_offset;
struct sfnt_compound_glyph_component *component;
gvar = blend->gvar;
if (gvar->axis_count != blend->fvar->axis_count)
return 1;
if (gvar->glyph_count <= id)
return 1;
if (gvar->flags & 1)
offset = gvar->u.offset_long[id];
else
offset = gvar->u.offset_word[id] * 2u;
if (offset >= gvar->data_size)
return 1;
end = gvar->glyph_variation_data + gvar->data_size;
/* Start reading the header. */
if (offset + sizeof header > gvar->data_size)
return 1;
/* Clear the distortion. */
distortion->origin = 0;
distortion->advance = 0;
memcpy (&header, gvar->glyph_variation_data + offset,
sizeof header);
/* Swap the header. */
sfnt_swap16 (&header.tuple_count);
sfnt_swap16 (&header.data_offset);
/* Prepare to read each tuple. */
ntuples = header.tuple_count & 0x0fff;
/* Initialize the data offset. This is incremented with each tuple
read. */
data_offset = header.data_offset;
/* If gvar->flags & tuples_share_point_numbers, read the shared
point numbers. */
npoints = 0;
if (header.tuple_count & 0x8000)
{
data = gvar->glyph_variation_data + offset + data_offset;
points = sfnt_read_packed_points (data, &npoints, end,
&tuple);
if (!points)
return 1;
/* Shared point numbers are part of the data after the tuple
array. Thus, increment data_offset by tuple - data. `tuple'
here holds no relation to a pointer to the current part of
the tuple array that is being read later on. */
data_offset += tuple - data;
}
else
points = NULL;
/* Start reading each tuple. */
tuple = gvar->glyph_variation_data + offset + sizeof header;
if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16)
coords = xmalloc (gvar->axis_count * sizeof *coords * 3);
else
coords = alloca (gvar->axis_count * sizeof *coords * 3);
intermediate_start = coords + gvar->axis_count;
intermediate_end = intermediate_start + gvar->axis_count;
while (ntuples--)
{
data = gvar->glyph_variation_data + offset + data_offset;
if (tuple + 3 >= end)
goto fail1;
memcpy (&data_size, tuple, sizeof data_size);
tuple += sizeof data_size;
memcpy (&index, tuple, sizeof index);
tuple += sizeof index;
sfnt_swap16 (&data_size);
sfnt_swap16 (&index);
/* Increment the offset to the data by the data size specified
here. */
data_offset += data_size;
if (index & 0x8000)
{
/* Embedded coordinates are present. Read each
coordinate and add it to the tuple. */
for (j = 0; j < gvar->axis_count; ++j)
{
if (tuple + 1 >= end)
goto fail1;
memcpy (&coords[j], tuple, sizeof *coords);
tuple += sizeof *coords;
sfnt_swap16 (&coords[j]);
}
}
else if ((index & 0xfff) > gvar->shared_coord_count)
/* index exceeds the number of shared tuples present. */
goto fail1;
else
/* index points into gvar->axis_count coordinates making up
the tuple. */
memcpy (coords, (gvar->global_coords
+ ((index & 0xfff) * gvar->axis_count)),
gvar->axis_count * sizeof *coords);
/* Now read indeterminate tuples if required. */
if (index & 0x4000)
{
for (j = 0; j < gvar->axis_count; ++j)
{
if (tuple + 1 >= end)
goto fail1;
memcpy (&intermediate_start[j], tuple,
sizeof *intermediate_start);
tuple += sizeof *intermediate_start;
sfnt_swap16 (&intermediate_start[j]);
}
for (j = 0; j < gvar->axis_count; ++j)
{
if (tuple + 1 >= end)
goto fail1;
memcpy (&intermediate_end[j], tuple,
sizeof *intermediate_end);
tuple += sizeof *intermediate_end;
sfnt_swap16 (&intermediate_end[j]);
}
}
/* See whether or not the tuple applies to the current variation
configuration, and how much to scale them by. */
scale = sfnt_compute_tuple_scale (blend, index & 0x4000,
coords, intermediate_start,
intermediate_end);
if (!scale)
continue;
local_points = NULL;
/* Finally, read private point numbers.
Set local_points to those numbers; it will be freed
once the loop ends. */
if (index & 0x2000)
{
local_points = sfnt_read_packed_points (data, &n_local_points,
end, &data);
if (!local_points)
goto fail1;
point_count = n_local_points;
glyph_points = local_points;
}
else
{
/* If there are no private point numbers, use global
points. */
point_count = npoints;
glyph_points = points;
}
/* Now, read packed deltas. */
dx = NULL;
dy = NULL;
switch (point_count)
{
case UINT16_MAX:
/* Deltas are provided for all components in the glyph. */
/* Add 4 phantom points to each end. */
dx = sfnt_read_packed_deltas (data, end,
glyph->compound->num_components + 4,
&data);
dy = sfnt_read_packed_deltas (data, end,
glyph->compound->num_components + 4,
&data);
if (!dx || !dy)
goto fail3;
/* Apply each delta to the compound glyph. */
for (i = 0; i < glyph->compound->num_components; ++i)
{
component = &glyph->compound->components[i];
/* Check if the component uses deltas at all. */
if (!(component->flags & 02))
continue;
/* Vary the X offset. */
if (!(component->flags & 01))
word = component->argument1.b;
else
word = component->argument1.d;
fword = sfnt_mul_fixed_round (dx[i], scale);
component->argument1.d = word + fword;
/* Vary the Y offset. */
if (!(component->flags & 01))
word = component->argument2.b;
else
word = component->argument2.d;
fword = sfnt_mul_fixed_round (dy[i], scale);
/* Set the flag that says offsets are words. */
component->flags |= 01;
component->argument2.d = word + fword;
}
/* Apply the deltas for the two phantom points. */
distortion->origin += sfnt_mul_fixed_round (dx[i++], scale);
distortion->advance += sfnt_mul_fixed_round (dx[i], scale);
break;
default:
dx = sfnt_read_packed_deltas (data, end, point_count, &data);
dy = sfnt_read_packed_deltas (data, end, point_count, &data);
if (!dx || !dy)
goto fail3;
/* Deltas are only applied for each point number read. */
for (i = 0; i < point_count; ++i)
{
/* Apply deltas to phantom points. */
if (glyph_points[i] == glyph->compound->num_components)
{
distortion->origin += sfnt_mul_fixed_round (dx[i], scale);
continue;
}
if (glyph_points[i] == glyph->compound->num_components + 1)
{
distortion->advance += sfnt_mul_fixed_round (dx[i], scale);
continue;
}
/* Make sure the point doesn't end up out of bounds. */
if (glyph_points[i] >= glyph->compound->num_components)
continue;
component = &glyph->compound->components[glyph_points[i]];
/* Check if the component uses deltas at all. */
if (!(component->flags & 02))
continue;
/* Vary the X offset. */
if (!(component->flags & 01))
word = component->argument1.b;
else
word = component->argument1.d;
fword = sfnt_mul_fixed_round (dx[i], scale);
component->argument1.d = word + fword;
/* Vary the Y offset. */
if (!(component->flags & 01))
word = component->argument2.b;
else
word = component->argument2.d;
fword = sfnt_mul_fixed_round (dy[i], scale);
/* Set the flag that says offsets are words. */
component->flags |= 01;
component->argument2.d = word + fword;
}
break;
}
xfree (dx);
xfree (dy);
if (local_points != (uint16_t *) -1)
xfree (local_points);
}
/* Return success. */
if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16)
xfree (coords);
if (points != (uint16_t *) -1)
xfree (points);
/* Set the glyph metrics distortion as well. */
glyph->advance_distortion = distortion->advance;
glyph->origin_distortion = distortion->origin;
return 0;
fail3:
xfree (dx);
xfree (dy);
xfree (local_points);
fail1:
if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16)
xfree (coords);
if (points != (uint16_t *) -1)
xfree (points);
return 1;
}
/* Vary the specified INTERPRETER's control value table using the
variations in BLEND's CVT variations table, then record the blend's
normalized coordinates and axis count in the interpreter.
The CVT table used to create INTERPRETER must be the same used
to read BLEND->cvar. If not, behavior is undefined. */
TEST_STATIC void
sfnt_vary_interpreter (struct sfnt_interpreter *interpreter,
struct sfnt_blend *blend)
{
sfnt_fixed scale;
int i;
struct sfnt_tuple_variation *variation;
size_t ndeltas, j, index;
sfnt_f26dot6 delta;
/* Return if there's no cvar table. */
if (!blend->cvar)
return;
/* For each tuple in the cvar table... */
for (i = 0; i < (blend->cvar->tuple_count & 0x0fff); ++i)
{
/* See if the tuple applies. */
variation = &blend->cvar->variation[i];
scale = sfnt_compute_tuple_scale (blend,
variation->intermediate_start != NULL,
variation->coordinates,
variation->intermediate_start,
variation->intermediate_end);
if (!scale)
continue;
/* Figure out how many deltas there are. If variation->points,
there are num_points deltas. Otherwise, there are
interpreter->cvt->num_elements deltas. */
ndeltas = (variation->points
? variation->num_points
: interpreter->cvt_size);
for (j = 0; j < ndeltas; ++j)
{
/* Figure out which CVT entry this applies to. */
index = variation->points ? variation->points[j] : j;
if (index > interpreter->cvt_size)
continue;
/* Multiply the delta by the interpreter scale factor and
then the tuple scale factor. */
delta = sfnt_mul_f26dot6_fixed (variation->deltas[j],
interpreter->scale);
delta = sfnt_mul_fixed_round (delta, scale);
/* Apply the delta to the control value table. */
interpreter->cvt[i] += delta;
}
}
interpreter->n_axis = blend->fvar->axis_count;
interpreter->norm_coords = blend->norm_coords;
}
/* OS/2 metadata retrieval.
A font's `OS/2' table incorporates some miscellaneous information
that is consulted by the font scaler on MS-Windows. Emacs requires
one fragment of this information: the font foundry name. */
/* Read an OS/2 table from the given font FD. Use the table directory
provided in SUBTABLE.
Return the OS/2 table if successful, NULL otherwise. */
TEST_STATIC struct sfnt_OS_2_table *
sfnt_read_OS_2_table (int fd, struct sfnt_offset_subtable *subtable)
{
struct sfnt_OS_2_table *OS_2;
struct sfnt_table_directory *directory;
ssize_t rc;
size_t minimum, wanted;
/* Search for the OS/2 table within SUBTABLE. */
directory = sfnt_find_table (subtable, SFNT_TABLE_OS_2);
if (!directory)
return NULL;
/* Calculate how large the table must be. The field `panose' is the
last field aligned to natural boundaries, and thus contents must
be read twice: once to populate the table with information up to
`panose', and once again to retrieve the information
afterwards. */
minimum = (SFNT_ENDOF (struct sfnt_OS_2_table, panose,
unsigned char[10])
+ SFNT_ENDOF (struct sfnt_OS_2_table, fs_last_char_index,
uint16_t)
- offsetof (struct sfnt_OS_2_table, ul_unicode_range));
/* If the table is too short, return. */
if (directory->length < minimum)
return NULL;
/* Seek to the location given in the directory. */
if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
return NULL;
OS_2 = xmalloc (sizeof *OS_2);
/* Read data into the structure. */
wanted = SFNT_ENDOF (struct sfnt_OS_2_table, fs_last_char_index,
uint16_t);
rc = read (fd, OS_2, wanted);
if (rc == -1 || rc != wanted)
{
xfree (OS_2);
return NULL;
}
/* Byte swap that data. */
sfnt_swap16 (&OS_2->version);
sfnt_swap16 (&OS_2->x_avg_char_width);
sfnt_swap16 (&OS_2->us_weight_class);
sfnt_swap16 (&OS_2->us_width_class);
sfnt_swap16 (&OS_2->fs_type);
sfnt_swap16 (&OS_2->y_subscript_x_size);
sfnt_swap16 (&OS_2->y_subscript_y_size);
sfnt_swap16 (&OS_2->y_subscript_x_offset);
sfnt_swap16 (&OS_2->y_subscript_y_offset);
sfnt_swap16 (&OS_2->y_superscript_x_size);
sfnt_swap16 (&OS_2->y_superscript_y_size);
sfnt_swap16 (&OS_2->y_superscript_x_offset);
sfnt_swap16 (&OS_2->y_superscript_y_offset);
sfnt_swap16 (&OS_2->y_strikeout_size);
sfnt_swap16 (&OS_2->y_strikeout_position);
sfnt_swap16 (&OS_2->s_family_class);
sfnt_swap32 (&OS_2->ul_unicode_range[0]);
sfnt_swap32 (&OS_2->ul_unicode_range[1]);
sfnt_swap32 (&OS_2->ul_unicode_range[2]);
sfnt_swap32 (&OS_2->ul_unicode_range[3]);
sfnt_swap16 (&OS_2->fs_selection);
sfnt_swap16 (&OS_2->fs_first_char_index);
sfnt_swap16 (&OS_2->fs_last_char_index);
return OS_2;
}
/* PostScript metadata retrieval.
TrueType fonts electively incorporate a table of miscellaneous
information concerning such matters as the underline position or
whether the font is fixed pitch. This table also assigns
human-readable names to glyphs, subject to the table format, but
these names are not read by the functions defined below. */
/* Read the header of a post table from the given font FD. Refer to
the table directory SUBTABLE for its location.
Return the post table header if successful, NULL otherwise. */
TEST_STATIC struct sfnt_post_table *
sfnt_read_post_table (int fd, struct sfnt_offset_subtable *subtable)
{
struct sfnt_post_table *post;
struct sfnt_table_directory *directory;
ssize_t rc;
/* Search for the post table within SUBTABLE. */
directory = sfnt_find_table (subtable, SFNT_TABLE_POST);
if (!directory)
return NULL;
/* Although the size of the table is affected by its format, this
function is meant to read only its header; guarantee that the
directory is that large. */
if (directory->length < sizeof *post)
return NULL;
/* Seek to the location given in the directory. */
if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
return NULL;
post = xmalloc (sizeof *post);
rc = read (fd, post, sizeof *post);
if (rc == -1 || rc != sizeof *post)
{
xfree (post);
return NULL;
}
/* Byte swap the data retrieved. */
sfnt_swap32 (&post->format);
sfnt_swap32 (&post->italic_angle);
sfnt_swap16 (&post->underline_position);
sfnt_swap16 (&post->underline_thickness);
sfnt_swap32 (&post->is_fixed_pitch);
sfnt_swap32 (&post->min_mem_type_42);
sfnt_swap32 (&post->max_mem_type_42);
sfnt_swap32 (&post->min_mem_type_1);
sfnt_swap32 (&post->max_mem_type_1);
return post;
}
#ifdef TEST
struct sfnt_test_dcontext
{
/* Context for sfnt_test_get_glyph. */
struct sfnt_glyf_table *glyf;
struct sfnt_loca_table_short *loca_short;
struct sfnt_loca_table_long *loca_long;
struct sfnt_hmtx_table *hmtx;
struct sfnt_hhea_table *hhea;
struct sfnt_maxp_table *maxp;
struct sfnt_blend *blend;
};
/* Global context for test functions. Height of glyph. */
static sfnt_fixed sfnt_test_max;
static void
sfnt_test_move_to (struct sfnt_point point, void *dcontext)
{
printf ("move_to: %g, %g\n", sfnt_coerce_fixed (point.x),
sfnt_coerce_fixed (point.y));
}
static void
sfnt_test_line_to (struct sfnt_point point, void *dcontext)
{
printf ("line_to: %g, %g\n", sfnt_coerce_fixed (point.x),
sfnt_coerce_fixed (point.y));
}
static void
sfnt_test_curve_to (struct sfnt_point control,
struct sfnt_point endpoint,
void *dcontext)
{
printf ("curve_to: %g, %g - %g, %g\n",
sfnt_coerce_fixed (control.x),
sfnt_coerce_fixed (control.y),
sfnt_coerce_fixed (endpoint.x),
sfnt_coerce_fixed (endpoint.y));
}
static struct sfnt_glyph *
sfnt_test_get_glyph (sfnt_glyph id, void *dcontext,
bool *need_free)
{
struct sfnt_test_dcontext *tables;
struct sfnt_glyph *glyph;
struct sfnt_metrics_distortion distortion;
tables = dcontext;
*need_free = true;
glyph = sfnt_read_glyph (id, tables->glyf,
tables->loca_short,
tables->loca_long);
if (tables->blend && glyph)
{
if (glyph->simple)
sfnt_vary_simple_glyph (tables->blend, id, glyph,
&distortion);
else
sfnt_vary_compound_glyph (tables->blend, id, glyph,
&distortion);
}
return glyph;
}
static void
sfnt_test_free_glyph (struct sfnt_glyph *glyph, void *dcontext)
{
sfnt_free_glyph (glyph);
}
static int
sfnt_test_get_metrics (sfnt_glyph glyph, struct sfnt_glyph_metrics *metrics,
void *dcontext)
{
struct sfnt_test_dcontext *tables;
tables = dcontext;
return sfnt_lookup_glyph_metrics (glyph, metrics,
tables->hmtx, tables->hhea,
tables->maxp);
}
static void
sfnt_test_span (struct sfnt_edge *edge, sfnt_fixed y,
void *dcontext)
{
#if 1
printf ("/* span at %g */\n", sfnt_coerce_fixed (y));
for (; edge; edge = edge->next)
{
if (y >= edge->bottom && y < edge->top)
printf ("ctx.fillRect (%g, %g, 1, 1); "
"/* %g top: %g bot: %g stepx: %g winding: %d */\n",
sfnt_coerce_fixed (edge->x),
sfnt_coerce_fixed (sfnt_test_max - y),
sfnt_coerce_fixed (y),
sfnt_coerce_fixed (edge->top),
sfnt_coerce_fixed (edge->bottom),
sfnt_coerce_fixed (edge->step_x),
edge->winding);
else
printf ("STRIPPED BAD SPAN!!! %g %g %"PRIi32
" %"PRIi32" (winding: %d)\n",
sfnt_coerce_fixed (edge->top),
sfnt_coerce_fixed (edge->bottom),
edge->top, y, edge->winding);
}
#elif 0
int winding;
short x, dx;
winding = 0;
x = 0;
for (; edge; edge = edge->next)
{
dx = (edge->x >> 16) - x;
x = edge->x >> 16;
for (; dx > 0; --dx)
putc (winding ? '.' : ' ', stdout);
winding = !winding;
}
putc ('\n', stdout);
#elif 0
for (; edge; edge = edge->next)
printf ("%g-", sfnt_coerce_fixed (edge->x));
puts ("");
#endif
}
static void
sfnt_test_edge_ignore (struct sfnt_edge *edges, size_t num_edges,
void *dcontext)
{
}
/* The same debugger stuff is used here. */
static void sfnt_setup_debugger (void);
/* The debugger's X display. */
static Display *display;
/* The debugger window. */
static Window window;
/* The GC. */
static GC point_gc, background_gc;
static void
sfnt_test_edges (struct sfnt_edge *edges, size_t num_edges)
{
static sfnt_fixed y;
size_t i;
for (i = 0; i < num_edges; ++i)
{
if (y >= edges[i].bottom && y < edges[i].top)
{
XDrawPoint (display, window, point_gc,
edges[i].x / 65536, 100 - (y / 65536));
printf ("sfnt_test_edges: %d %d\n",
edges[i].x / 65536, 100 - (y / 65536));
}
}
y += SFNT_POLY_STEP;
for (i = 0; i < num_edges; ++i)
sfnt_step_edge (&edges[i]);
}
static void
sfnt_debug_edges (struct sfnt_edge *edges, size_t num_edges)
{
XEvent event;
sfnt_setup_debugger ();
while (true)
{
XNextEvent (display, &event);
switch (event.type)
{
case KeyPress:
XDestroyWindow (display, window);
XCloseDisplay (display);
exit (0);
break;
case Expose:
while (true)
{
sfnt_test_edges (edges, num_edges);
XFlush (display);
usleep (50000);
}
break;
}
}
}
static void
sfnt_test_edge (struct sfnt_edge *edges, size_t num_edges,
void *dcontext)
{
size_t i;
printf ("built %zu edges\n", num_edges);
for (i = 0; i < num_edges; ++i)
{
printf ("/* edge x, top, bot: %g, %g - %g. winding: %d */\n"
"/* edge step_x: %g */\n",
sfnt_coerce_fixed (edges[i].x),
sfnt_coerce_fixed (edges[i].top),
sfnt_coerce_fixed (edges[i].bottom),
edges[i].winding,
sfnt_coerce_fixed (edges[i].step_x));
#ifdef TEST_VERTEX
printf ("ctx.fillRect (%g, %g, 1, 1);\n",
sfnt_coerce_fixed (edges[i].x),
sfnt_coerce_fixed (sfnt_test_max
- edges[i].y));
#else
printf ("ctx.fillRect (%g, %g, 1, 1);\n",
sfnt_coerce_fixed (edges[i].x),
sfnt_coerce_fixed (sfnt_test_max
- edges[i].bottom));
#endif
}
if (getenv ("SFNT_DEBUG_STEP"))
{
if (!fork ())
sfnt_debug_edges (edges, num_edges);
}
printf ("==end of edges==\n");
sfnt_poly_edges (edges, num_edges, sfnt_test_span, NULL);
}
static void
sfnt_x_raster (struct sfnt_raster **rasters,
int *advances,
int nrasters,
struct sfnt_hhea_table *hhea,
sfnt_fixed scale)
{
Display *display;
Window window;
Pixmap *pixmaps;
Picture *glyphs, drawable, solid;
int event_base, error_base;
int major, minor, *depths, count;
XRenderPictFormat *format, *glyph_format;
Visual *visual;
XImage image;
GC gc;
XGCValues gcvalues;
XEvent event;
XRenderColor white, black;
int i, ascent, origin, x, y;
Font font;
if (!nrasters)
exit (0);
display = XOpenDisplay (NULL);
if (!display)
exit (0);
if (!XRenderQueryExtension (display, &event_base, &error_base)
|| !XRenderQueryVersion (display, &major, &minor))
exit (0);
if (major == 0 && minor < 10)
exit (0);
window = XCreateSimpleWindow (display, DefaultRootWindow (display),
0, 0, 100, 150, 0, 0,
WhitePixel (display,
DefaultScreen (display)));
XSelectInput (display, window, ExposureMask);
XMapWindow (display, window);
visual = DefaultVisual (display, DefaultScreen (display));
format = XRenderFindVisualFormat (display, visual);
if (!format)
exit (0);
glyph_format = XRenderFindStandardFormat (display, PictStandardA8);
depths = XListDepths (display, DefaultScreen (display), &count);
for (i = 0; i < count; ++i)
{
if (depths[i] == 8)
goto depth_found;
}
exit (0);
depth_found:
XFree (depths);
pixmaps = alloca (sizeof *pixmaps * nrasters);
glyphs = alloca (sizeof *glyphs * nrasters);
gc = None;
for (i = 0; i < nrasters; ++i)
{
pixmaps[i] = XCreatePixmap (display, DefaultRootWindow (display),
rasters[i]->width, rasters[i]->height, 8);
if (!gc)
gc = XCreateGC (display, pixmaps[i], 0, &gcvalues);
/* Upload the raster. */
image.width = rasters[i]->width;
image.height = rasters[i]->height;
image.xoffset = 0;
image.format = ZPixmap;
image.data = (char *) rasters[i]->cells;
image.byte_order = MSBFirst;
image.bitmap_unit = 8;
image.bitmap_bit_order = LSBFirst;
image.bitmap_pad = SFNT_POLY_ALIGNMENT * 8;
image.depth = 8;
image.bytes_per_line = rasters[i]->stride;
image.bits_per_pixel = 8;
image.red_mask = 0;
image.green_mask = 0;
image.blue_mask = 0;
if (!XInitImage (&image))
abort ();
XPutImage (display, pixmaps[i], gc, &image,
0, 0, 0, 0, image.width, image.height);
glyphs[i] = XRenderCreatePicture (display, pixmaps[i],
glyph_format, 0, NULL);
}
XFreeGC (display, gc);
font = XLoadFont (display, "6x13");
if (!font)
exit (1);
gcvalues.font = font;
gcvalues.foreground = BlackPixel (display, DefaultScreen (display));
gc = XCreateGC (display, window, GCForeground | GCFont, &gcvalues);
drawable = XRenderCreatePicture (display, window, format,
0, NULL);
memset (&black, 0, sizeof black);
black.alpha = 65535;
solid = XRenderCreateSolidFill (display, &black);
while (true)
{
XNextEvent (display, &event);
if (event.type == Expose)
{
white.red = 65535;
white.green = 65535;
white.blue = 65535;
white.alpha = 65535;
/* Clear the background. */
XRenderFillRectangle (display, PictOpSrc, drawable,
&white, 0, 0, 65535, 65535);
/* Compute ascent line. */
ascent = sfnt_mul_fixed (hhea->ascent * 65536,
scale) / 65536;
origin = 5;
for (i = 0; i < nrasters; ++i)
{
/* Compute the base position. */
x = origin + rasters[i]->offx;
y = ascent - rasters[i]->height - rasters[i]->offy;
/* Draw the solid fill with the glyph as clip mask. */
XRenderComposite (display, PictOpOver, solid, glyphs[i],
drawable, 0, 0, 0, 0, x, y,
rasters[i]->width, rasters[i]->height);
origin += advances[i];
}
}
}
}
static void
sfnt_test_raster (struct sfnt_raster *raster,
struct sfnt_hhea_table *hhea,
sfnt_fixed scale)
{
int x, y, i;
for (y = 0; y < raster->height; ++y)
{
for (x = 0; x < raster->width; ++x)
printf ("%3d ", (int) raster->cells[y * raster->stride + x]);
puts ("");
}
if (hhea && getenv ("SFNT_X"))
{
i = 0;
if (!fork ())
sfnt_x_raster (&raster, &i, 1, hhea, scale);
}
}
/* Instruction execution tests. */
static struct sfnt_maxp_table test_interpreter_profile =
{
0x00010000,
650,
100,
100,
100,
100,
2,
100,
255,
12,
12,
100,
5000,
100,
1,
};
static sfnt_fword test_cvt_values[] =
{
100, 100, -100, -100, 50, 50, 50, 50, 0, 0,
};
static struct sfnt_cvt_table test_interpreter_cvt =
{
10,
test_cvt_values,
};
static struct sfnt_head_table test_interpreter_head =
{
0x00010000,
0x00010000,
0,
0x5f0f3cf5,
0,
800,
0,
0,
0,
0,
-312,
-555,
1315,
2163,
0,
12,
0,
0,
0,
};
static struct sfnt_interpreter *
sfnt_make_test_interpreter (void)
{
return sfnt_make_interpreter (&test_interpreter_profile,
&test_interpreter_cvt,
&test_interpreter_head,
NULL, 17, 17);
}
struct sfnt_interpreter_test
{
const char *name;
unsigned char *instructions;
int num_instructions;
void *arg;
void (*check) (struct sfnt_interpreter *, void *, bool);
};
static void
sfnt_run_interpreter_test (struct sfnt_interpreter_test *test,
struct sfnt_interpreter *interpreter)
{
fprintf (stderr, "Testing %s: ", test->name);
if (setjmp (interpreter->trap))
test->check (interpreter, test->arg, true);
else
{
interpreter->IP = 0;
interpreter->SP = interpreter->stack;
interpreter->instructions = test->instructions;
interpreter->num_instructions = test->num_instructions;
sfnt_interpret_run (interpreter, SFNT_RUN_CONTEXT_TEST);
test->check (interpreter, test->arg, false);
}
}
struct sfnt_generic_test_args
{
uint32_t *expected_stack;
int expected_stack_elements;
bool expected_trap;
int expected_IP;
};
static void
sfnt_generic_check (struct sfnt_interpreter *interpreter,
void *arg, bool trap)
{
struct sfnt_generic_test_args *args;
int i;
args = arg;
if (((interpreter->SP - interpreter->stack)
!= args->expected_stack_elements))
{
fprintf (stderr,
"failed at IP %d:%d (expected %d stack elements,"
" got %td); last trap string: %s\n",
interpreter->call_depth, interpreter->IP,
args->expected_stack_elements,
interpreter->SP - interpreter->stack,
((trap && interpreter->trap_reason)
? interpreter->trap_reason
: "NULL"));
for (i = 0; i < interpreter->SP - interpreter->stack; ++i)
fprintf (stderr, "%8d ", (int) interpreter->stack[i]);
fprintf (stderr, "\n");
return;
}
if (memcmp (interpreter->stack, args->expected_stack,
((char *) interpreter->SP
- (char *) interpreter->stack)))
{
fprintf (stderr, "failed (inconsistent stack elements)\n"
"machine stack ------------------------->\n");
for (i = 0; i < args->expected_stack_elements; ++i)
fprintf (stderr, "%8d ", (int) interpreter->stack[i]);
fprintf (stderr,
"\nexpected stack ------------------------>\n");
for (i = 0; i < args->expected_stack_elements; ++i)
fprintf (stderr, "%8d ", (int) args->expected_stack[i]);
fprintf (stderr, "\n");
return;
}
if (args->expected_IP != -1
&& interpreter->IP != args->expected_IP)
{
fprintf (stderr, "failed (IP is %d, not %d)\n",
interpreter->IP, args->expected_IP);
return;
}
if (trap)
{
if (args->expected_trap)
fprintf (stderr, "passed (with trap %s)\n",
interpreter->trap_reason);
else
fprintf (stderr, "failed (unexpected trap %s)\n",
interpreter->trap_reason);
return;
}
if (args->expected_trap)
fprintf (stderr, "failed, trap not encountered\n");
else
fprintf (stderr, "passed\n");
return;
}
static void
sfnt_check_srp0 (struct sfnt_interpreter *interpreter,
void *arg, bool trap)
{
if (trap)
{
fprintf (stderr, "failed (unexpected trap %s)\n",
interpreter->trap_reason);
return;
}
if (interpreter->state.rp0 != 0)
{
fprintf (stderr, "failed, rp0 is not 0, but %d\n",
interpreter->state.rp0);
return;
}
if (interpreter->state.rp1 != 1)
{
fprintf (stderr, "failed, rp1 is not 1, but %d\n",
interpreter->state.rp1);
return;
}
if (interpreter->state.rp2 != 2)
{
fprintf (stderr, "failed, rp2 is not 2, but %d\n",
interpreter->state.rp2);
return;
}
if (interpreter->SP != interpreter->stack)
{
fprintf (stderr, "failed, stack not empty\n");
return;
}
fprintf (stderr, "passed\n");
return;
}
static void
sfnt_check_szp0 (struct sfnt_interpreter *interpreter,
void *arg, bool trap)
{
if (!trap)
{
fprintf (stderr, "failed, expected trap\n");
return;
}
if (interpreter->state.zp0 != 1
|| interpreter->state.zp1 != 1
|| interpreter->state.zp2 != 0)
{
fprintf (stderr,
"failed, unexpected values of zone pointers: %d %d %d\n",
interpreter->state.zp0, interpreter->state.zp1,
interpreter->state.zp2);
return;
}
if (interpreter->SP != interpreter->stack)
{
fprintf (stderr, "failed, stack not empty\n");
return;
}
fprintf (stderr, "passed with expected trap %s\n",
interpreter->trap_reason);
return;
}
static void
sfnt_check_sloop (struct sfnt_interpreter *interpreter,
void *arg, bool trap)
{
if (interpreter->state.loop != 1)
{
/* The trap should've restored GS->loop to 1. */
fprintf (stderr, "failed, GS->loop should be 1, not %d\n",
interpreter->state.loop);
return;
}
if (!trap)
{
fprintf (stderr, "failed, expected trap\n");
return;
}
if (interpreter->SP != interpreter->stack)
{
fprintf (stderr, "failed, stack not empty\n");
return;
}
fprintf (stderr, "passed with expected trap %s\n",
interpreter->trap_reason);
return;
}
struct sfnt_rounding_test_args
{
sfnt_f26dot6 value;
};
static void
sfnt_check_rounding (struct sfnt_interpreter *interpreter,
void *arg, bool trap)
{
sfnt_f26dot6 value;
struct sfnt_rounding_test_args *args;
if (trap)
{
fprintf (stderr, "failed, unexpected trap: %s\n",
interpreter->trap_reason);
return;
}
if (interpreter->SP == interpreter->stack)
{
fprintf (stderr, "failed, empty stack\n");
return;
}
value = *(interpreter->SP - 1);
args = arg;
if (value != args->value)
{
fprintf (stderr, "failed. value is: %d %d, but wanted: %d %d\n",
value >> 6, value & 63, args->value >> 6,
args->value & 63);
return;
}
fprintf (stderr, "passed, expected value %d\n", value);
return;
}
static void
sfnt_check_smd (struct sfnt_interpreter *interpreter,
void *arg, bool trap)
{
if (trap)
{
fprintf (stderr, "failed, unexpected trap\n");
return;
}
if (interpreter->state.minimum_distance != 32)
{
fprintf (stderr, "failed, expected minimum distance"
" of 32, got %d\n",
interpreter->state.minimum_distance);
return;
}
fprintf (stderr, "passed\n");
return;
}
static void
sfnt_check_scvtci (struct sfnt_interpreter *interpreter,
void *arg, bool trap)
{
if (trap)
{
fprintf (stderr, "failed, unexpected trap\n");
return;
}
if (interpreter->state.cvt_cut_in != 128)
{
fprintf (stderr, "failed, expected 128, got %d\n",
interpreter->state.cvt_cut_in);
return;
}
fprintf (stderr, "passed\n");
return;
}
static void
sfnt_check_sswci (struct sfnt_interpreter *interpreter,
void *arg, bool trap)
{
if (trap)
{
fprintf (stderr, "failed, unexpected trap\n");
return;
}
if (interpreter->state.sw_cut_in != 512)
{
fprintf (stderr, "failed, expected 512, got %d\n",
interpreter->state.sw_cut_in);
return;
}
fprintf (stderr, "passed\n");
return;
}
static void
sfnt_check_ssw (struct sfnt_interpreter *interpreter,
void *arg, bool trap)
{
if (trap)
{
fprintf (stderr, "failed, unexpected trap\n");
return;
}
if (interpreter->state.single_width_value
!= sfnt_mul_f26dot6_fixed (-1, interpreter->scale))
{
fprintf (stderr, "failed, got %d at scale %d,"
" expected %d\n",
interpreter->state.single_width_value,
interpreter->scale,
sfnt_mul_f26dot6_fixed (-1, interpreter->scale));
return;
}
fprintf (stderr, "passed\n");
return;
}
static void
sfnt_check_flipon (struct sfnt_interpreter *interpreter,
void *arg, bool trap)
{
if (trap)
{
fprintf (stderr, "failed, unexpected trap\n");
return;
}
if (!interpreter->state.auto_flip)
fprintf (stderr, "failed, auto flip not enabled\n");
else
fprintf (stderr, "pass\n");
return;
}
static void
sfnt_check_flipoff (struct sfnt_interpreter *interpreter,
void *arg, bool trap)
{
if (trap)
{
fprintf (stderr, "failed, unexpected trap\n");
return;
}
if (interpreter->state.auto_flip)
fprintf (stderr, "failed, auto flip not disabled\n");
else
fprintf (stderr, "pass\n");
return;
}
static void
sfnt_check_sdb (struct sfnt_interpreter *interpreter,
void *arg, bool trap)
{
if (trap)
{
fprintf (stderr, "failed, unexpected trap %s\n",
interpreter->trap_reason);
return;
}
if (interpreter->state.delta_base != 8)
fprintf (stderr, "failed, delta base is %d, not 8\n",
interpreter->state.delta_base);
else
fprintf (stderr, "pass\n");
return;
}
static void
sfnt_check_sds (struct sfnt_interpreter *interpreter,
void *arg, bool trap)
{
if (trap)
{
fprintf (stderr, "failed, unexpected trap %s\n",
interpreter->trap_reason);
return;
}
if (interpreter->state.delta_shift != 1)
fprintf (stderr, "failed, delta shift is %d, not 1\n",
interpreter->state.delta_shift);
else
fprintf (stderr, "pass\n");
return;
}
static void
sfnt_check_scanctrl (struct sfnt_interpreter *interpreter,
void *arg, bool trap)
{
if (trap)
{
fprintf (stderr, "failed, unexpected trap %s\n",
interpreter->trap_reason);
return;
}
if (interpreter->SP != interpreter->stack)
{
fprintf (stderr, "failed, expected empty stack\n");
return;
}
if (interpreter->state.scan_control != 1)
fprintf (stderr, "failed, scan control is %d, not 1\n",
interpreter->state.scan_control);
else
fprintf (stderr, "pass\n");
return;
}
static void
sfnt_check_instctrl (struct sfnt_interpreter *interpreter,
void *arg, bool trap)
{
if (trap)
{
fprintf (stderr, "failed, unexpected trap %s\n",
interpreter->trap_reason);
return;
}
if (interpreter->SP != interpreter->stack)
{
fprintf (stderr, "failed, expected empty stack\n");
return;
}
if (interpreter->state.instruct_control != 2)
fprintf (stderr, "failed, inst control is %d, not 2\n",
interpreter->state.instruct_control);
else
fprintf (stderr, "pass\n");
return;
}
static struct sfnt_generic_test_args npushb_test_args =
{
(uint32_t []) { 1U, 2U, 3U, 4U, },
4,
true,
6,
};
static struct sfnt_generic_test_args npushw_test_args =
{
(uint32_t []) { 0x101U, 0x202U, 0x303U, 0x404U, },
4,
true,
10,
};
static struct sfnt_generic_test_args pushb_test_args =
{
(uint32_t []) { 1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U,
1U, },
9,
true,
11,
};
static struct sfnt_generic_test_args pushw_test_args =
{
(uint32_t []) { 0x203U, 0x204U, 0x205U, 0x206U, 0x207U, 0x208U,
0x909U, 0x909U, (uint32_t) -1, },
9,
true,
20,
};
static struct sfnt_generic_test_args stack_overflow_test_args =
{
NULL,
0,
true,
0,
};
static struct sfnt_generic_test_args stack_underflow_test_args =
{
NULL,
0,
true,
4,
};
static struct sfnt_rounding_test_args rtg_test_args =
{
64,
};
static struct sfnt_rounding_test_args rtg_symmetric_test_args =
{
-64,
};
static struct sfnt_rounding_test_args rtg_1_test_args =
{
0,
};
static struct sfnt_rounding_test_args rtg_1_symmetric_test_args =
{
0,
};
static struct sfnt_rounding_test_args rthg_test_args =
{
32,
};
static struct sfnt_rounding_test_args rthg_1_test_args =
{
96,
};
static struct sfnt_rounding_test_args rtdg_test_args =
{
32,
};
static struct sfnt_rounding_test_args rtdg_1_test_args =
{
0,
};
static struct sfnt_rounding_test_args rtdg_2_test_args =
{
32,
};
static struct sfnt_rounding_test_args rtdg_3_test_args =
{
64,
};
static struct sfnt_generic_test_args else_test_args =
{
(uint32_t []) { 77U, 90U, 83U, },
3,
false,
40,
};
static struct sfnt_generic_test_args jmpr_test_args =
{
/* What ends up on the stack?
First, there are the three words that the first PUSHW[2]
instruction has pushed:
0, 0xb2, -3
After those three words are pushed, JMPR[] is called, and pops an
offset:
-3
so now the stack is:
0, 0xb2
as a result of the relative jump, IP is now at the least
significant byte of the word inside what was originally a
PUSHW[2] instruction, 0xb2, which itself is PUSHB[2]!
As a result of that instruction, three more bytes, including
JMPR[] itself are pushed onto the stack, making it:
0, 0xb2, 255, 253, 0x1c
Then, execution continues as usual. 4 is pushed on to the
stack, making it:
0, 0xb2, 255, 253, 0x1c, 4
Another JMPR[] pops:
4
making the stack:
0, 0xb2, 255, 253, 0x1c
And skips the next three padding bytes, finally reaching a
PUSHW[0] instruction which pushes -30 onto the stack:
0, 0xb2, 255, 253, 0x1c, -30
and a JMPR[] instruction, which pops:
-30
making:
0, 0xb2, 255, 253,
and subsequently traps, as -30 would underflow the instruction
stream. */
(uint32_t []) { 0, 0xb2, 255, 253, 0x1c, },
5,
true,
17,
};
static struct sfnt_generic_test_args dup_test_args =
{
NULL,
0,
true,
5,
};
static struct sfnt_generic_test_args pop_test_args =
{
(uint32_t []) { 70, 70, },
2,
false,
5,
};
static struct sfnt_generic_test_args clear_test_args =
{
NULL,
0,
false,
10,
};
static struct sfnt_generic_test_args swap_test_args =
{
(uint32_t []) { 2, 1, },
2,
false,
4,
};
static struct sfnt_generic_test_args depth_test_args =
{
(uint32_t []) { 3, 3, 3, 3, },
4,
false,
5,
};
static struct sfnt_generic_test_args cindex_test_args =
{
(uint32_t []) { 0, 3, 3, 4, 0, },
5,
true,
10,
};
static struct sfnt_generic_test_args mindex_test_args =
{
(uint32_t []) { 0, 3, 7, 4, 4, },
5,
false,
10,
};
static struct sfnt_generic_test_args raw_test_args =
{
NULL,
0,
true,
0,
};
static struct sfnt_generic_test_args loopcall_test_args =
{
(uint32_t []) { 10, },
1,
false,
12,
};
static struct sfnt_generic_test_args call_test_args =
{
(uint32_t []) { 11, },
1,
true,
2,
};
static struct sfnt_generic_test_args fdef_test_args =
{
NULL,
0,
true,
4,
};
static struct sfnt_generic_test_args fdef_1_test_args =
{
NULL,
0,
true,
9,
};
static struct sfnt_generic_test_args endf_test_args =
{
NULL,
0,
true,
0,
};
static struct sfnt_generic_test_args ws_test_args =
{
(uint32_t []) { 40, },
1,
true,
10,
};
static struct sfnt_generic_test_args rs_test_args =
{
NULL,
0,
true,
2,
};
static struct sfnt_generic_test_args wcvtp_test_args =
{
(uint32_t []) { 32, },
1,
true,
10,
};
static struct sfnt_generic_test_args rcvt_test_args =
{
(uint32_t []) { 136, },
1,
true,
5,
};
static struct sfnt_generic_test_args mppem_test_args =
{
(uint32_t []) { 17, },
1,
false,
1,
};
static struct sfnt_generic_test_args mps_test_args =
{
(uint32_t []) { 17, },
1,
false,
1,
};
static struct sfnt_generic_test_args debug_test_args =
{
NULL,
0,
false,
3,
};
static struct sfnt_generic_test_args lt_test_args =
{
(uint32_t []) { 1, 0, 0, },
3,
false,
12,
};
static struct sfnt_generic_test_args lteq_test_args =
{
(uint32_t []) { 1, 0, 1, },
3,
false,
12,
};
static struct sfnt_generic_test_args gt_test_args =
{
(uint32_t []) { 0, 1, 0, },
3,
false,
12,
};
static struct sfnt_generic_test_args gteq_test_args =
{
(uint32_t []) { 0, 1, 1, },
3,
false,
12,
};
static struct sfnt_generic_test_args eq_test_args =
{
(uint32_t []) { 0, 1, 0, },
3,
false,
18,
};
static struct sfnt_generic_test_args neq_test_args =
{
(uint32_t []) { 1, 0, 1, },
3,
false,
18,
};
static struct sfnt_generic_test_args odd_test_args =
{
(uint32_t []) { 1, 0, },
2,
false,
9,
};
static struct sfnt_generic_test_args even_test_args =
{
(uint32_t []) { 0, 1, },
2,
false,
9,
};
static struct sfnt_generic_test_args if_test_args =
{
(uint32_t []) { 17, 24, 1, 2, 3, 4, 5, -1, -1,
88, 1, 3, },
12,
false,
185,
};
static struct sfnt_generic_test_args eif_test_args =
{
NULL,
0,
false,
3,
};
static struct sfnt_generic_test_args and_test_args =
{
(uint32_t []) { 0, 0, 1, 0, },
4,
false,
16,
};
static struct sfnt_generic_test_args or_test_args =
{
(uint32_t []) { 1, 1, 1, 0, },
4,
false,
16,
};
static struct sfnt_generic_test_args not_test_args =
{
(uint32_t []) { 0, 1, },
2,
false,
6,
};
static struct sfnt_generic_test_args sds_test_args =
{
NULL,
0,
true,
5,
};
static struct sfnt_generic_test_args add_test_args =
{
(uint32_t []) { 96, -1, },
2,
false,
10,
};
static struct sfnt_generic_test_args sub_test_args =
{
(uint32_t []) { 64, -64, 431, },
3,
false,
14,
};
static struct sfnt_generic_test_args div_test_args =
{
(uint32_t []) { 32, -64, },
2,
true,
15,
};
static struct sfnt_generic_test_args mul_test_args =
{
(uint32_t []) { 255, -255, 255, },
3,
false,
16,
};
static struct sfnt_generic_test_args abs_test_args =
{
(uint32_t []) { 1, 1, },
2,
false,
7,
};
static struct sfnt_generic_test_args neg_test_args =
{
(uint32_t []) { 1, -1, },
2,
false,
7,
};
static struct sfnt_generic_test_args floor_test_args =
{
(uint32_t []) { -128, -64, 0, 64, 128, },
5,
false,
17,
};
static struct sfnt_generic_test_args ceiling_test_args =
{
(uint32_t []) { -128, -128, -64, 0, 64, 128, 128, },
7,
false,
25,
};
static struct sfnt_generic_test_args round_test_args =
{
NULL,
0,
true,
0,
};
static struct sfnt_generic_test_args nround_test_args =
{
(uint32_t []) { 63, },
1,
false,
3,
};
static struct sfnt_generic_test_args wcvtf_test_args =
{
(uint32_t []) { (63 * 17 * 65535 / 800) >> 10, },
1,
false,
7,
};
static struct sfnt_generic_test_args jrot_test_args =
{
(uint32_t []) { 40, 40, },
2,
false,
13,
};
static struct sfnt_generic_test_args jrof_test_args =
{
(uint32_t []) { 4, },
1,
false,
13,
};
static struct sfnt_generic_test_args deltac1_test_args =
{
(uint32_t []) { ((((50 * 17 * 65535) + 32767) / 800) >> 10) + 8,
((((50 * 17 * 65535) + 32767) / 800) >> 10) + 8, },
2,
false,
22,
};
static struct sfnt_generic_test_args deltac2_test_args =
{
(uint32_t []) { ((((50 * 17 * 65535) + 32767) / 800) >> 10) + 8,
((((50 * 17 * 65535) + 32767) / 800) >> 10) + 8, },
2,
false,
22,
};
static struct sfnt_generic_test_args deltac3_test_args =
{
(uint32_t []) { ((((50 * 17 * 65535) + 32767) / 800) >> 10) + 8,
((((50 * 17 * 65535) + 32767) / 800) >> 10) + 8, },
2,
false,
22,
};
/* Macros and instructions for detailed rounding tests. */
/* PUSHB[0] period:phase:threshold
SROUND[] */
#define SFNT_ROUNDING_OPERAND(period, phase, threshold) \
0xb0, (((unsigned char) period << 6) \
| ((unsigned char) phase & 3) << 4 \
| ((unsigned char) threshold & 15)), 0x76
/* PUSHB[0] period:phase:threshold
S45ROUND[] */
#define SFNT_ROUNDING_OPERAND_45(period, phase, threshold) \
0xb0, (((unsigned char) period << 6) \
| ((unsigned char) phase & 3) << 4 \
| ((unsigned char) threshold & 15)), 0x77
/* PUSHB[0] value
ROUND[] */
#define SFNT_ROUND_VALUE(value) 0xb0, value, 0x68
static unsigned char sfnt_sround_instructions[] =
{
SFNT_ROUNDING_OPERAND (0, 0, 8),
SFNT_ROUND_VALUE (15),
SFNT_ROUND_VALUE (17),
SFNT_ROUNDING_OPERAND (1, 0, 8),
SFNT_ROUND_VALUE (32),
SFNT_ROUND_VALUE (16),
SFNT_ROUNDING_OPERAND (2, 0, 8),
SFNT_ROUND_VALUE (64),
SFNT_ROUND_VALUE (63),
SFNT_ROUNDING_OPERAND (0, 1, 8),
SFNT_ROUND_VALUE (16),
SFNT_ROUND_VALUE (24),
SFNT_ROUNDING_OPERAND (0, 2, 8),
SFNT_ROUND_VALUE (20),
SFNT_ROUND_VALUE (48),
SFNT_ROUNDING_OPERAND (0, 3, 8),
SFNT_ROUND_VALUE (7),
SFNT_ROUND_VALUE (70),
};
static uint32_t sfnt_sround_values[] =
{
/* 0, 0, 8 = RTDG; 15 rounded to the double grid and becomes 0, 17
is 32. */
0, 32,
/* 1, 0, 8 = RTG; 32 rounded to the grid is 64, 16 is 0. */
64, 0,
/* 2, 0, 8 = round to a grid separated by 128s. 64 is 128, 63 is
0. */
128, 0,
/* 0, 1, 8 = round to a double grid with a phase of 8. 16 rounds
down to 8, 24 rounds up to 40. */
8, 40,
/* 0, 2, 8 = round to a double grid with a phase of 16. 20 rounds
down to 16, 40 rounds up to 48. */
16, 48,
/* 0, 3, 8 = round to a double grid with a phase of 48. 7 rounds
up to 16, 70 rounds up to 80. */
16, 80,
};
static struct sfnt_generic_test_args sround_test_args =
{
sfnt_sround_values,
ARRAYELTS (sfnt_sround_values),
false,
ARRAYELTS (sfnt_sround_instructions),
};
static unsigned char sfnt_s45round_instructions[] =
{
SFNT_ROUNDING_OPERAND_45 (0, 0, 0),
SFNT_ROUND_VALUE (1),
SFNT_ROUND_VALUE (45),
};
static uint32_t sfnt_s45round_values[] =
{
/* 0, 0, 0: 1 rounded to the double cubic grid becomes 45, and 46
rounded to the double cubic grid becomes 90. */
45, 90,
};
static struct sfnt_generic_test_args s45round_test_args =
{
sfnt_s45round_values,
ARRAYELTS (sfnt_s45round_values),
false,
ARRAYELTS (sfnt_s45round_instructions),
};
static struct sfnt_generic_test_args rutg_test_args =
{
(uint32_t []) { 64, 64, 0, },
3,
false,
10,
};
static struct sfnt_generic_test_args rdtg_test_args =
{
(uint32_t []) { 0, 0, 64, },
3,
false,
10,
};
static struct sfnt_generic_test_args sangw_test_args =
{
NULL,
0,
false,
3,
};
static struct sfnt_generic_test_args aa_test_args =
{
NULL,
0,
false,
3,
};
static struct sfnt_generic_test_args getinfo_test_args =
{
/* Pretend to be the Macintosh System 7 scaler.
This lets the interpreter get away with only two phantom
points, as specified in Apple's TrueType reference manual. */
(uint32_t []) { 2, 0, },
2,
false,
6,
};
static struct sfnt_generic_test_args idef_test_args =
{
(uint32_t []) { 1, 2, 3, },
3,
false,
11,
};
static struct sfnt_generic_test_args roll_test_args =
{
(uint32_t []) { 1, 2, 4, 5, 3, },
5,
false,
7,
};
static struct sfnt_generic_test_args roll_1_test_args =
{
(uint32_t []) { 1, 2, },
2,
true,
3,
};
static struct sfnt_generic_test_args max_test_args =
{
(uint32_t []) { 70, },
1,
false,
6,
};
static struct sfnt_generic_test_args min_test_args =
{
(uint32_t []) { -70, },
1,
false,
6,
};
static struct sfnt_generic_test_args scantype_test_args =
{
NULL,
0,
false,
3,
};
static struct sfnt_interpreter_test all_tests[] =
{
{
"NPUSHB",
/* NPUSHB[] 4 1 2 3 4
NPUSHB[] 5 1 2 3 4 */
(unsigned char []) { 0x40, 4, 1, 2, 3, 4,
0x40, 5, 1, 2, 3, 4, },
10,
&npushb_test_args,
sfnt_generic_check,
},
{
"NPUSHW",
/* NPUSHW[] 4 0x101 0x202 0x303 0x404
NPUSHW[] 4 0x101 0x202 0x303 0x4?? */
(unsigned char []) { 0x41, 4, 1, 1, 2, 2, 3, 3, 4, 4,
0x41, 4, 1, 1, 2, 2, 3, 3, 4, },
19,
&npushw_test_args,
sfnt_generic_check,
},
{
"PUSHB",
/* PUSHB[7] 1 2 3 4 5 6 7 8
PUSHB[0] 1
PUSHB[5] 1 2 3 4 5 ? */
(unsigned char []) { 0xb7, 1, 2, 3, 4, 5, 6, 7, 8,
0xb0, 1,
0xb5, 1, 2, 3, 4, 5, },
17,
&pushb_test_args,
sfnt_generic_check,
},
{
"PUSHW",
/* PUSHW[7] 2 3 2 4 2 5 2 6 2 7 2 8 9 9 9 9
PUSHW[0] 255 255 -- this should get sign-extended
PUSHW[5] 1 1 2 2 3 3 4 4 5 5 6 ? */
(unsigned char []) { 0xbf, 2, 3, 2, 4, 2, 5, 2, 6, 2, 7, 2, 8, 9, 9, 9, 9,
0xb8, 255, 255,
0xbc, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, },
28,
&pushw_test_args,
sfnt_generic_check,
},
{
"that stack overflow is handled correctly",
/* NPUSHB[] 101 0... */
(unsigned char [103]) { 0x40, 101, },
103,
&stack_overflow_test_args,
sfnt_generic_check,
},
{
"that stack underflow is handled correctly",
/* PUSHW[0] 100 100
POP[]
POP[] */
(unsigned char []) { 0xb8, 100, 100,
0x21,
0x21, },
5,
&stack_underflow_test_args,
sfnt_generic_check,
},
{
"SRP0, SRP1, SRP2",
/* PUSHB[0] 0
SRP0[]
PUSHB[0] 1
SRP1[]
PUSHB[0] 2
SRP2[] */
(unsigned char []) { 0xb0, 0,
0x10,
0xb0, 1,
0x11,
0xb0, 2,
0x12, },
9,
NULL,
sfnt_check_srp0,
},
{
"SZP0, SZP1, SZP2, SZPS",
/* PUSHB[0] 1
SZP0[]
PUSHB[0] 1
SZP1[]
PUSHB[0] 0
SZP2[]
PUSHB[0] 5
SZPS[] */
(unsigned char []) { 0xb0, 1,
0x13,
0xb0, 1,
0x14,
0xb0, 0,
0x15,
0xb0, 5,
0x16, },
12,
NULL,
sfnt_check_szp0,
},
{
"SLOOP",
/* PUSHB[0] 2
SLOOP[]
PUSHW[0] 255 255 (-1)
SLOOP[] */
(unsigned char []) { 0xb0, 2,
0x17,
0xb8, 255, 255,
0x17, },
7,
NULL,
sfnt_check_sloop,
},
{
"RTG",
/* RTG[]
PUSHB[0] 32
ROUND[] */
(unsigned char []) { 0x18,
0xb0, 32,
0x68, },
4,
&rtg_test_args,
sfnt_check_rounding,
},
{
"rounding symmetry",
/* RTG[]
PUSHW[0] 255 -32
ROUND[] */
(unsigned char []) { 0x18,
0xb8, 255, - (signed char) 32,
0x68, },
5,
&rtg_symmetric_test_args,
sfnt_check_rounding,
},
{
"RTG to 0",
/* RTG[]
PUSHB[0] 31
ROUND[] */
(unsigned char []) { 0x18,
0xb0, 31,
0x68, },
4,
&rtg_1_test_args,
sfnt_check_rounding,
},
{
"rounding symmetry to 0",
/* RTG[]
PUSHB[0] 255 -31
ROUND[] */
(unsigned char []) { 0x18,
0xb8, 255, - (signed char) 31,
0x68, },
5,
&rtg_1_symmetric_test_args,
sfnt_check_rounding,
},
{
"RTHG",
/* RTHG[]
PUSHB[0] 0
ROUND[] */
(unsigned char []) { 0x19,
0xb0, 0,
0x68, },
4,
&rthg_test_args,
sfnt_check_rounding,
},
{
"RTHG to 96",
/* RTHG[]
PUSHB[0] 64
ROUND[] */
(unsigned char []) { 0x19,
0xb0, 64,
0x68, },
4,
&rthg_1_test_args,
sfnt_check_rounding,
},
{
"SMD",
/* PUSHB[0] 32
SMD[] */
(unsigned char []) { 0xb0, 32,
0x1a, },
3,
NULL,
sfnt_check_smd,
},
{
"ELSE",
/* ELSE[]
;; Lots of variable length instructions
;; which will not be executed, like:
NPUSHW[] 3 11 22 33 44 55 66
NPUSHB[] 1 3
PUSHW[2] 1 1 2 2 3 3
PUSHB[2] 1 2 3
;; Also test nested ifs.
PUSHW[0] 1 1
IF[]
PUSHW[0] 1 1
ELSE[]
PUSHW[0] 1 1
EIF[]
EIF[]
PUSHW[0] 1 1
;; the actual contents of the stack.
PUSHB[2] 77 90 83 */
(unsigned char []) { 0x1b,
0x41, 3, 11, 22, 33, 44, 55, 66,
0x40, 1, 3,
0xba, 1, 1, 2, 2, 3, 3,
0xb2, 1, 2, 3,
0xb8, 1, 1,
0x58,
0xb8, 1, 1,
0x1b,
0xb8, 1, 1,
0x59,
0x59,
0xb2, 77, 90, 83, },
40,
&else_test_args,
sfnt_generic_check,
},
{
"JMPR",
/* PUSHW[2] 00 00 00 PUSHB[2] 255 253 JMPR[]
PUSHB[0] 4
JMPR[]
255 255 255
PUSHW[0] 255 -30
JMPR[] */
(unsigned char []) { 0xba, 00, 00, 00, 0xb2, 255, 253, 0x1c,
0xb0, 4,
0x1c,
255, 255, 255,
0xb8, 255, -30,
0x1c, },
18,
&jmpr_test_args,
sfnt_generic_check,
},
{
"SCVTCI",
/* PUSHB[0] 128
SCVTCI[] */
(unsigned char []) { 0xb0, 128,
0x1d, },
3,
NULL,
sfnt_check_scvtci,
},
{
"SSWCI",
/* PUSHW[0] 2 0 ;; 512
SSWCI[] */
(unsigned char []) { 0xb8, 2, 0,
0x1e, },
4,
NULL,
sfnt_check_sswci,
},
{
"SSW",
/* PUSHW[0] 255 255 ; -1
SSW[] ; this should be converted to device-space */
(unsigned char []) { 0xb8, 255, 255,
0x1f, },
4,
NULL,
sfnt_check_ssw,
},
{
"DUP",
/* PUSHB[0] 70
DUP[]
POP[]
POP[]
DUP[] */
(unsigned char []) { 0xb0, 70,
0x20,
0x21,
0x21,
0x70, },
6,
&dup_test_args,
sfnt_generic_check,
},
{
"POP",
/* PUSHB[0] 70
DUP[]
DUP[]
POP[] */
(unsigned char []) { 0xb0, 70,
0x20,
0x20,
0x21, },
5,
&pop_test_args,
sfnt_generic_check,
},
{
"CLEAR",
/* PUSHB[7] 1 2 3 4 5 6 7 8
CLEAR[] */
(unsigned char []) { 0xb7, 1, 2, 3, 4, 5, 6, 7, 8,
0x22, },
10,
&clear_test_args,
sfnt_generic_check,
},
{
"SWAP",
/* PUSHB[1] 1 2
SWAP[] */
(unsigned char []) { 0xb1, 1, 2,
0x23, },
4,
&swap_test_args,
sfnt_generic_check,
},
{
"DEPTH",
/* PUSHB[2] 3 3 3
DEPTH[] */
(unsigned char []) { 0xb2, 3, 3, 3,
0x24, },
5,
&depth_test_args,
sfnt_generic_check,
},
{
"CINDEX",
/* PUSHB[4] 0 3 3 4 1
CINDEX[] ; pops 1, indices 4
CINDEX[] ; pops 4, indices 0
PUSHB[0] 6
CINDEX[] ; pops 6, trap */
(unsigned char []) { 0xb4, 0, 3, 3, 4, 1,
0x25,
0x25,
0xb0, 6,
0x25, },
11,
&cindex_test_args,
sfnt_generic_check,
},
{
"MINDEX",
/* PUSHB[6] 0 3 4 7 3 4 2
MINDEX[] ; pops 2, array becomes 0 3 4 7 4 3
MINDEX[] ; pops 3, array becomes 0 3 7 4 4 */
(unsigned char []) { 0xb6, 0, 3, 4, 7, 3, 4, 2,
0x26,
0x26, },
10,
&mindex_test_args,
sfnt_generic_check,
},
{
"RAW",
/* RAW[] */
(unsigned char []) { 0x28, },
1,
&raw_test_args,
sfnt_generic_check,
},
{
"LOOPCALL",
/* PUSHB[1] 0 2
FDEF[]
PUSHB[0] 1
ADD[]
ENDF[]
PUSHB[1] 10 2
LOOPCALL[] */
(unsigned char []) { 0xb1, 0, 2,
0x2c,
0xb0, 1,
0x60,
0x2d,
0xb1, 10, 2,
0x2a, },
12,
&loopcall_test_args,
sfnt_generic_check,
},
{
"CALL",
/* PUSHB[1] 7 2
FDEF[]
PUSHB[0] 1
ADD[]
ENDF[]
PUSHB[0] 2
CALL[]
PUSHB[0] 3
ADD[]
;; Test that infinite recursion fails.
PUSHB[0] 3
FDEF[]
PUSHB[0] 3
CALL[]
ENDF[]
PUSHB[0] 3
CALL[] */
(unsigned char []) { 0xb1, 7, 2,
0x2c,
0xb0, 1,
0x60,
0x2d,
0xb0, 2,
0x2b,
0xb0, 3,
0x60,
0xb0, 3,
0x2c,
0xb0, 3,
0x2b,
0x2d,
0xb0, 3,
0x2b, },
24,
&call_test_args,
sfnt_generic_check,
},
{
"that FDEF traps inside nested definitions",
/* PUSHB[0] 1
FDEF[]
FDEF[]
ENDF[]
ENDF[] */
(unsigned char []) { 0xb0, 1,
0x2c,
0x2c,
0x2d,
0x2d, },
6,
&fdef_test_args,
sfnt_generic_check,
},
{
"that FDEF traps upon missing ENDF",
/* PUSHB[0] 1
FDEF[]
PUSHB[3] 1 2 3 4
POP[] */
(unsigned char []) { 0xb0, 1,
0x2c,
0xb3, 1, 2, 3, 4,
0x21, },
9,
&fdef_1_test_args,
sfnt_generic_check,
},
{
"ENDF",
/* ENDF[] */
(unsigned char []) { 0x2d, },
1,
&endf_test_args,
sfnt_generic_check,
},
{
"RTDG",
/* RTDG[]
PUSHB[0] 16
ROUND[] */
(unsigned char []) { 0x3d,
0xb0, 16,
0x68, },
4,
&rtdg_test_args,
sfnt_check_rounding,
},
{
"RTDG down to 0",
/* RTDG[]
PUSHB[0] 15
ROUND[] */
(unsigned char []) { 0x3d,
0xb0, 15,
0x68, },
4,
&rtdg_1_test_args,
sfnt_check_rounding,
},
{
"RTDG down to 32",
/* RTDG[]
PUSHB[0] 47
ROUND[] */
(unsigned char []) { 0x3d,
0xb0, 47,
0x68, },
4,
&rtdg_2_test_args,
sfnt_check_rounding,
},
{
"RTDG up to 64",
/* RTDG[]
PUSHB[0] 48
ROUND[] */
(unsigned char []) { 0x3d,
0xb0, 48,
0x68, },
4,
&rtdg_3_test_args,
sfnt_check_rounding,
},
{
"WS",
/* PUSHB[1] 240 40
WS[]
PUSHB[0] 240
RS[]
PUSHB[1] 255 40
WS[] */
(unsigned char []) { 0xb1, 240, 40,
0x42,
0xb0, 240,
0x43,
0xb1, 255, 40,
0x42, },
11,
&ws_test_args,
sfnt_generic_check,
},
{
"RS",
/* PUSHB[0] 255
RS[] */
(unsigned char []) { 0xb0, 255,
0x43, },
3,
&rs_test_args,
sfnt_generic_check,
},
{
"WCVTP",
/* PUSHB[1] 9 32
WCVTP[]
PUSHB[0] 9
RCVT[]
PUSHB[1] 10 10
WCVTP[] */
(unsigned char []) { 0xb1, 9, 32,
0x44,
0xb0, 9,
0x45,
0xb1, 10, 10,
0x44, },
11,
&wcvtp_test_args,
sfnt_generic_check,
},
{
"RCVT",
/* PUSHB[0] 1
RCVT[]
PUSHB[0] 10
RCVT[] */
(unsigned char []) { 0xb0, 1,
0x45,
0xb0, 10,
0x45, },
6,
&rcvt_test_args,
sfnt_generic_check,
},
{
"MPPEM",
/* MPPEM[] */
(unsigned char []) { 0x4b, },
1,
&mppem_test_args,
sfnt_generic_check,
},
{
"MPS",
/* MPS[] */
(unsigned char []) { 0x4c, },
1,
&mps_test_args,
sfnt_generic_check,
},
{
"FLIPON",
/* FLIPON[] */
(unsigned char []) { 0x4d, },
1,
NULL,
sfnt_check_flipon,
},
{
"FLIPOFF",
/* FLIPOFF[] */
(unsigned char []) { 0x4e, },
1,
NULL,
sfnt_check_flipoff,
},
{
"DEBUG",
/* PUSHB[0] 1
DEBUG[] */
(unsigned char []) { 0xb0, 1,
0x4f, },
3,
&debug_test_args,
sfnt_generic_check,
},
{
"LT",
/* PUSHB[1] 47 48
LT[]
PUSHB[1] 48 47
LT[]
PUSHB[1] 47 47
LT[] */
(unsigned char []) { 0xb1, 47, 48,
0x50,
0xb1, 48, 47,
0x50,
0xb1, 47, 47,
0x50, },
12,
&lt_test_args,
sfnt_generic_check,
},
{
"LTEQ",
/* PUSHB[1] 47 48
LTEQ[]
PUSHB[1] 48 47
LTEQ[]
PUSHB[1] 47 47
LTEQ[] */
(unsigned char []) { 0xb1, 47, 48,
0x51,
0xb1, 48, 47,
0x51,
0xb1, 47, 47,
0x51, },
12,
&lteq_test_args,
sfnt_generic_check,
},
{
"GT",
/* PUSHB[1] 47 48
GT[]
PUSHB[1] 48 47
GT[]
GT[1] 47 47
LTEQ[] */
(unsigned char []) { 0xb1, 47, 48,
0x52,
0xb1, 48, 47,
0x52,
0xb1, 47, 47,
0x52, },
12,
&gt_test_args,
sfnt_generic_check,
},
{
"GTEQ",
/* PUSHB[1] 47 48
GTEQ[]
PUSHB[1] 48 47
GTEQ[]
GTEQ[1] 47 47
LTEQ[] */
(unsigned char []) { 0xb1, 47, 48,
0x53,
0xb1, 48, 47,
0x53,
0xb1, 47, 47,
0x53, },
12,
&gteq_test_args,
sfnt_generic_check,
},
{
"EQ",
/* PUSHW[1] 255 253 255 255
EQ[]
PUSHW[1] 27 27 27 27
EQ[]
PUSHB[0] 3
PUSHW[0] 255 254
EQ[] */
(unsigned char []) { 0xb9, 255, 253, 255, 255,
0x54,
0xb9, 27, 27, 27, 27,
0x54,
0xb0, 3,
0xb8, 255, 254,
0x54, },
18,
&eq_test_args,
sfnt_generic_check,
},
{
"NEQ",
/* PUSHW[1] 255 253 255 255
NEQ[]
PUSHW[1] 27 27 27 27
NEQ[]
PUSHB[0] 3
PUSHW[0] 255 254
NEQ[] */
(unsigned char []) { 0xb9, 255, 253, 255, 255,
0x55,
0xb9, 27, 27, 27, 27,
0x55,
0xb0, 3,
0xb8, 255, 254,
0x55, },
18,
&neq_test_args,
sfnt_generic_check,
},
{
"ODD",
/* RTG[]
PUSHW[1] 255 224 ;; -32
ODD[] ;; Rounds symmetrically to -64, which is odd.
PUSHW[1] 255 159 ;; -96
ODD[] ;; Rounds symmetrically to -128, which is even. */
(unsigned char []) { 0x18,
0xb8, 255, 224,
0x56,
0xb8, 255, 159,
0x56, },
9,
&odd_test_args,
sfnt_generic_check,
},
{
"EVEN",
/* RTG[]
PUSHW[1] 255 224 ;; -32
EVEN[] ;; Rounds symmetrically to -64, which is odd.
PUSHW[1] 255 159 ;; -96
EVEN[] ;; Rounds symmetrically to -128, which is even. */
(unsigned char []) { 0x18,
0xb8, 255, 224,
0x57,
0xb8, 255, 159,
0x57, },
9,
&even_test_args,
sfnt_generic_check,
},
{
"IF",
/* NPUSHB[] 1 0
IF[]
PUSHW[0] 1 1
PUSHW[1] 1 1 2 2
PUSHW[2] 1 1 2 2 3 3
PUSHW[3] 1 1 2 2 3 3 4 4
PUSHW[4] 1 1 2 2 3 3 4 4 5 5
PUSHW[5] 1 1 2 2 3 3 4 4 5 5 6 6
PUSHW[6] 1 1 2 2 3 3 4 4 5 5 6 6 7 7
PUSHW[7] 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8
PUSHB[0] 1
PUSHB[1] 2 1
PUSHB[2] 3 2 1
PUSHB[3] 4 3 2 1
PUSHB[4] 5 4 3 2 1
PUSHB[5] 6 5 4 3 2 1
PUSHB[6] 7 6 5 4 3 2 1
PUSHB[7] 8 7 6 5 4 3 2 1
DEBUG[]
IF[]
PUSHB[7] 12 12 12 12 12 12 12 12
ELSE[]
EIF[]
ELSE[]
PUSHB[1] 17 24
NPUSHB[] 5 1 2 3 4 5
NPUSHW[] 2 255 255 255 255
EIF[]
PUSHB[0] 1
IF[]
NPUSHB[] 2 43 43
IF[]
PUSHB[0] 45
ELSE[]
PUSHB[0] 14
EIF[]
ADD[]
ELSE[]
NPUSHB[] 4 3 2 1 0
EIF[]
PUSHB[1] 1 3 */
(unsigned char []) { 0x40, 1, 0,
0x58,
0xb8, 1, 1,
0xb9, 1, 1, 2, 2,
0xba, 1, 1, 2, 2, 3, 3,
0xbb, 1, 1, 2, 2, 3, 3, 4, 4,
0xbc, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5,
0xbd, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
0xbe, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7,
0xbf, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
0xb0, 1,
0xb1, 2, 1,
0xb2, 3, 2, 1,
0xb3, 4, 3, 2, 1,
0xb4, 5, 4, 3, 2, 1,
0xb5, 6, 5, 4, 3, 2, 1,
0xb6, 7, 6, 5, 4, 3, 2, 1,
0xb7, 8, 7, 6, 5, 4, 3, 2, 1,
0x4f,
0x58,
0xb7, 12, 12, 12, 12, 12, 12, 12, 12,
0x1b,
0x59,
0x1b,
0xb1, 17, 24,
0x40, 5, 1, 2, 3, 4, 5,
0x41, 2, 255, 255, 255, 255,
0x59,
0xb0, 1,
0x58,
0x40, 2, 43, 43,
0x58,
0xb0, 45,
0x1b,
0xb0, 14,
0x59,
0x60,
0x1b,
0x40, 4, 3, 2, 1, 0,
0x59,
0xb1, 1, 3, },
185,
&if_test_args,
sfnt_generic_check,
},
{
"EIF",
/* PUSHB[0] 1
IF[]
EIF[] */
(unsigned char []) { 0xb0, 1,
0x58,
0x59, },
3,
&eif_test_args,
sfnt_generic_check,
},
{
"AND",
/* PUSHB[1] 0 1
AND[]
PUSHB[1] 37 0
AND[]
PUSHB[1] 40 1
AND[]
PUSHB[1] 0 0
AND[] */
(unsigned char []) { 0xb1, 0, 1,
0x5a,
0xb1, 37, 0,
0x5a,
0xb1, 40, 1,
0x5a,
0xb1, 0, 0,
0x5a, },
16,
&and_test_args,
sfnt_generic_check,
},
{
"OR",
/* PUSHB[1] 0 1
OR[]
PUSHB[1] 37 0
OR[]
PUSHB[1] 40 1
OR[]
PUSHB[1] 0 0
OR[] */
(unsigned char []) { 0xb1, 0, 1,
0x5b,
0xb1, 37, 0,
0x5b,
0xb1, 40, 1,
0x5b,
0xb1, 0, 0,
0x5b, },
16,
&or_test_args,
sfnt_generic_check,
},
{
"NOT",
/* PUSHB[0] 1
NOT[]
PUSHB[0] 0
NOT[] */
(unsigned char []) { 0xb0, 1,
0x5c,
0xb0, 0,
0x5c, },
6,
&not_test_args,
sfnt_generic_check,
},
{
"SDB",
/* PUSHB[0] 8
SDB[] */
(unsigned char []) { 0xb0, 8,
0x5e, },
3,
NULL,
sfnt_check_sdb,
},
{
"SDS",
/* PUSHB[0] 1
SDS[] */
(unsigned char []) { 0xb0, 1,
0x5f, },
3,
NULL,
sfnt_check_sds,
},
{
"that SDS rejects invalid values",
/* PUSHB[0] 1,
SDS[]
PUSHB[0] 7
SDS[] */
(unsigned char []) { 0xb0, 1,
0x5f,
0xb0, 7,
0x5f, },
6,
&sds_test_args,
sfnt_generic_check,
},
{
"ADD",
/* PUSHB[1] 64 32
ADD[]
PUSHW[1] 255 40 0 215 ;; -216 + 215
ADD[] */
(unsigned char []) { 0xb1, 64, 32,
0x60,
0xb9, 255, 40, 0, 215,
0x60, },
10,
&add_test_args,
sfnt_generic_check,
},
{
"SUB",
/* PUSHB[1] 96 32
SUB[]
PUSHB[1] 32 96
SUB[]
PUSHW[1] 0 215 255 40 ;; 215 - -216
SUB[] */
(unsigned char []) { 0xb1, 96, 32,
0x61,
0xb1, 32, 96,
0x61,
0xb9, 0, 215, 255, 40,
0x61, },
14,
&sub_test_args,
sfnt_generic_check,
},
{
"DIV",
/* PUSHB[1] 64 128
DIV[] ; 1 / 2 = 0.5
PUSHW[1] 0 32 255 224
DIV[] ; 0.5 / -0.5 = -1.0
PUSHW[1] 255 255 0 0
DIV[] ; -1 / 0 = trap */
(unsigned char []) { 0xb1, 64, 128,
0x62,
0xb9, 0, 32, 255, 224,
0x62,
0xb9, 255, 255, 0, 0,
0x62, },
16,
&div_test_args,
sfnt_generic_check,
},
{
"MUL",
/* PUSHB[1] 255 64
MUL[] ; 255 * 1 = 255
PUSHW[1] 0 255 255 192
MUL[] ; 255 * -1 = -255
PUSHW[1] 255 1 255 192
MUL[] ; -255 * -1 = 255 */
(unsigned char []) { 0xb1, 255, 64,
0x63,
0xb9, 0, 255, 255, 192,
0x63,
0xb9, 255, 1, 255, 192,
0x63, },
16,
&mul_test_args,
sfnt_generic_check,
},
{
"ABS",
/* PUSHW[0] 255 255
ABS[] ;; abs (-1) == 1
PUSHB[0] 1
ABS[] ;; abs (1) == 1 */
(unsigned char []) { 0xb8, 255, 255,
0x64,
0xb0, 1,
0x64, },
7,
&abs_test_args,
sfnt_generic_check,
},
{
"NEG",
/* PUSHW[0] 255 255
NEG[] ;; neg (-1) == 1
PUSHB[0] 1
NEG[] ;; neg (1) == -1 */
(unsigned char []) { 0xb8, 255, 255,
0x65,
0xb0, 1,
0x65, },
7,
&neg_test_args,
sfnt_generic_check,
},
{
"FLOOR",
/* PUSHW[0] 255 129 ; -127
FLOOR[] ; floor (-127) == -128
PUSHW[0] 255 193 ; -63
FLOOR[] ; floor (-63) == -64
PUSHB[0] 63
FLOOR[] ; floor (63) == 0
PUSHB[0] 127
FLOOR[] ; floor (127) == 64
PUSHB[0] 191
FLOOR[] ; floor (191) == 128 */
(unsigned char []) { 0xb8, 255, 129,
0x66,
0xb8, 255, 193,
0x66,
0xb0, 63,
0x66,
0xb0, 127,
0x66,
0xb0, 191,
0x66, },
17,
&floor_test_args,
sfnt_generic_check,
},
{
"CEILING",
/* PUSHW[0] 255 128 ; -128
CEILING[] ; ceiling (-128) == -128
PUSHW[0] 255 127 ; -129
CEILING[] ; ceiling (-129) == -128
PUSHW[0] 255 191 ; -65
CEILING[] ; ceiling (-65) == -64
PUSHW[0] 255 255 ; -1
CEILING[] ; ceiling (-1) == 0
PUSHB[0] 63
CEILING[] ; ceiling (63) == 64
PUSHB[0] 65
CEILING[] ; ceiling (65) == 128
PUSHB[0] 128
CEILING[] ; ceiling (128) == 128 */
(unsigned char []) { 0xb8, 255, 128,
0x67,
0xb8, 255, 127,
0x67,
0xb8, 255, 191,
0x67,
0xb8, 255, 255,
0x67,
0xb0, 63,
0x67,
0xb0, 65,
0x67,
0xb0, 128,
0x67, },
25,
&ceiling_test_args,
sfnt_generic_check,
},
{
"ROUND",
/* ROUND[] */
(unsigned char []) { 0x68, },
1,
&round_test_args,
sfnt_generic_check,
},
{
"NROUND",
/* PUSHB[0] 63
NROUND[] */
(unsigned char []) { 0xb0, 63,
0x6c, },
3,
&nround_test_args,
sfnt_generic_check,
},
{
"WCVTF",
/* PUSHB[1] 1 63
WCVTF[]
PUSHB[0] 1
RCVT[] */
(unsigned char []) { 0xb1, 1, 63,
0x70,
0xb0, 1,
0x45, },
7,
&wcvtf_test_args,
sfnt_generic_check,
},
{
"JROT",
/* PUSHB[1] 4 0
JROT[] ; this should not skip past the next instruction
PUSHB[1] 40 40
PUSHB[1] 3 1
JROT[] ; this should skip past the next instruction
PUSHB[0] 4 */
(unsigned char []) { 0xb1, 4, 0,
0x78,
0xb1, 40, 40,
0xb1, 3, 1,
0x78,
0xb0, 4, },
13,
&jrot_test_args,
sfnt_generic_check,
},
{
"JROF",
/* PUSHB[1] 4 0
JROF[] ; this should skip past the next instruction
PUSHB[1] 40 40
PUSHB[1] 3 1
JROF[] ; this should not skip past the next instruction
PUSHB[0] 4 */
(unsigned char []) { 0xb1, 4, 0,
0x79,
0xb1, 40, 40,
0xb1, 3, 1,
0x79,
0xb0, 4, },
13,
&jrof_test_args,
sfnt_generic_check,
},
{
"DELTAC1",
/* PUSHB[0] 2
SDB[] ; delta base now 2
PUSHB[0] 6
SDS[] ; delta shift now 6
PUSHB[2] 0xff 5 1 ; CVT index 5, ppem 15 + 2, magnitude 15
DELTAC1[]
PUSHB[0] 1
RCVT[] ; CVT index 5 should now be greater by 8 / 64
PUSHB[2] 0xef 5 1 ; CVT index 5, ppem 14 + 2, magnitude 15
DELTAC1[]
PUSHB[0] 1
RCVT[] ; CVT index 5 should be unchanged */
(unsigned char []) { 0xb0, 2,
0x5e,
0xb0, 6,
0x5f,
0xb2, 255, 5, 1,
0x73,
0xb0, 5,
0x45,
0xb2, 239, 5, 1,
0x73,
0xb0, 5,
0x45, },
22,
&deltac1_test_args,
sfnt_generic_check,
},
{
"DELTAC2",
/* PUSHB[0] 2
SDB[] ; delta base now 2
PUSHB[0] 6
SDS[] ; delta shift now 6
PUSHB[2] 0xff 5 1 ; CVT index 5, ppem 15 + 2 + 16, magnitude 15
DELTAC2[]
PUSHB[0] 1
RCVT[] ; CVT index 5 should be unchanged
PUSHB[2] 0xef 5 1 ; CVT index 5, ppem 14 + 2 + 16, magnitude 15
DELTAC2[]
PUSHB[0] 1
RCVT[] ; CVT index 5 should be unchanged */
(unsigned char []) { 0xb0, 2,
0x5e,
0xb0, 6,
0x5f,
0xb2, 255, 5, 1,
0x74,
0xb0, 5,
0x45,
0xb2, 239, 5, 1,
0x74,
0xb0, 5,
0x45, },
22,
&deltac2_test_args,
sfnt_generic_check,
},
{
"DELTAC3",
/* PUSHB[0] 2
SDB[] ; delta base now 2
PUSHB[0] 6
SDS[] ; delta shift now 6
PUSHB[2] 0xff 5 1 ; CVT index 5, ppem 15 + 2 + 32, magnitude 15
DELTAC3[]
PUSHB[0] 1
RCVT[] ; CVT index 5 should be unchanged
PUSHB[2] 0xef 5 1 ; CVT index 5, ppem 14 + 2 + 32, magnitude 15
DELTAC3[]
PUSHB[0] 1
RCVT[] ; CVT index 5 should be unchanged */
(unsigned char []) { 0xb0, 2,
0x5e,
0xb0, 6,
0x5f,
0xb2, 255, 5, 1,
0x75,
0xb0, 5,
0x45,
0xb2, 239, 5, 1,
0x75,
0xb0, 5,
0x45, },
22,
&deltac3_test_args,
sfnt_generic_check,
},
{
"SROUND",
sfnt_sround_instructions,
ARRAYELTS (sfnt_sround_instructions),
&sround_test_args,
sfnt_generic_check,
},
{
"S45ROUND",
sfnt_s45round_instructions,
ARRAYELTS (sfnt_s45round_instructions),
&s45round_test_args,
sfnt_generic_check,
},
{
"RUTG",
/* RUTG[]
PUSHB[0] 1
ROUND[]
PUSHB[0] 64
ROUND[]
PUSHB[0] 0
ROUND[] */
(unsigned char []) { 0x7c,
0xb0, 1,
0x68,
0xb0, 64,
0x68,
0xb0, 0,
0x68, },
10,
&rutg_test_args,
sfnt_generic_check,
},
{
"RDTG",
/* RUTG[]
PUSHB[0] 1
ROUND[]
PUSHB[0] 63
ROUND[]
PUSHB[0] 64
ROUND[] */
(unsigned char []) { 0x7d,
0xb0, 1,
0x68,
0xb0, 63,
0x68,
0xb0, 64,
0x68, },
10,
&rdtg_test_args,
sfnt_generic_check,
},
{
"SANGW",
/* PUSHB[0] 3
SANGW[] */
(unsigned char []) { 0xb0, 3,
0x7e, },
3,
&sangw_test_args,
sfnt_generic_check,
},
{
"AA",
/* PUSHB[0] 3
AA[] */
(unsigned char []) { 0xb0, 3,
0x7f, },
3,
&aa_test_args,
sfnt_generic_check,
},
{
"SCANCTRL",
/* PUSHB[0] 1
SCANCTRL[] */
(unsigned char []) { 0xb0, 1,
0x85, },
3,
NULL,
sfnt_check_scanctrl,
},
{
"GETINFO",
/* PUSHB[0] 1
GETINFO[]
PUSHB[0] 6
GETINFO[] */
(unsigned char []) { 0xb0, 1,
0x88,
0xb0, 6,
0x88, },
6,
&getinfo_test_args,
sfnt_generic_check,
},
{
"IDEF",
/* PUSHB[0] 0x83
IDEF[]
PUSHB[3] 1 2 3 4
POP[]
ENDF[]
0x83 */
(unsigned char []) { 0xb0, 0x83,
0x89,
0xb3, 1, 2, 3, 4,
0x21,
0x2d,
0x83, },
11,
&idef_test_args,
sfnt_generic_check,
},
{
"ROLL",
/* PUSHB[4] 1 2 3 4 5
ROLL[] ; this should become 1 2 4 5 3 */
(unsigned char []) { 0xb4, 1, 2, 3, 4, 5,
0x8a, },
7,
&roll_test_args,
sfnt_generic_check,
},
{
"that ROLL correctly handles underflow",
/* PUSHB[1] 1 2
ROLL[] */
(unsigned char []) { 0xb1, 1, 2,
0x8a, },
4,
&roll_1_test_args,
sfnt_generic_check,
},
{
"MAX",
/* PUSHW[1] 0 70 255 186 ; 70, -70
MAX[] */
(unsigned char []) { 0xb9, 0, 70, 255, 186,
0x8b, },
6,
&max_test_args,
sfnt_generic_check,
},
{
"MIN",
/* PUSHW[1] 0 70 255 186 ; 70, -70
MIN[] */
(unsigned char []) { 0xb9, 0, 70, 255, 186,
0x8c, },
6,
&min_test_args,
sfnt_generic_check,
},
{
"SCANTYPE",
/* PUSHB[0] 0
SCANTYPE[] */
(unsigned char []) { 0xb0, 0,
0x8d, },
3,
&scantype_test_args,
sfnt_generic_check,
},
{
"INSTCTRL",
/* PUSHB[1] 1 1
INSTCTRL[] ; (1 << 1) should now be set
PUSHB[1] 2 1
INSTCTRL[] ; (1 << 2) should now be set
PUSHB[1] 2 0
INSTCTRL[] ; (1 << 2) should no longer be set */
(unsigned char []) { 0xb1, 1, 1,
0x8e,
0xb1, 2, 1,
0x8e,
0xb1, 2, 0,
0x8e, },
12,
NULL,
sfnt_check_instctrl,
},
};
/* Instruction debugger. */
static void
sfnt_setup_debugger (void)
{
XGCValues gcv;
Font font;
display = XOpenDisplay (NULL);
if (!display)
exit (1);
window = XCreateSimpleWindow (display, DefaultRootWindow (display),
0, 0, 200, 200, 0, 0,
WhitePixel (display,
DefaultScreen (display)));
XMapWindow (display, window);
/* Select for the appropriate events. */
XSelectInput (display, window, KeyPressMask | ExposureMask);
/* Find an appropriate font. */
font = XLoadFont (display, "6x13");
if (!font)
exit (1);
/* The debugger has been set up. Set up the GCs for drawing points
and backgrounds. */
gcv.foreground = BlackPixel (display, DefaultScreen (display));
gcv.font = font;
point_gc = XCreateGC (display, window, GCForeground | GCFont,
&gcv);
gcv.foreground = WhitePixel (display, DefaultScreen (display));
background_gc = XCreateGC (display, window, GCForeground, &gcv);
}
static const char *
sfnt_name_instruction (unsigned char opcode)
{
static const char *const opcode_names[256] = {
"7 SVTCA y",
"7 SVTCA x",
"8 SPvTCA y",
"8 SPvTCA x",
"8 SFvTCA y",
"8 SFvTCA x",
"8 SPvTL ||",
"7 SPvTL +",
"8 SFvTL ||",
"7 SFvTL +",
"5 SPvFS",
"5 SFvFS",
"3 GPv",
"3 GFv",
"6 SFvTPv",
"5 ISECT",
"4 SRP0",
"4 SRP1",
"4 SRP2",
"4 SZP0",
"4 SZP1",
"4 SZP2",
"4 SZPS",
"5 SLOOP",
"3 RTG",
"4 RTHG",
"3 SMD",
"4 ELSE",
"4 JMPR",
"6 SCvTCi",
"5 SSwCi",
"3 SSW",
"3 DUP",
"3 POP",
"5 CLEAR",
"4 SWAP",
"5 DEPTH",
"6 CINDEX",
"6 MINDEX",
"8 AlignPTS",
"7 INS_$28",
"3 UTP",
"8 LOOPCALL",
"4 CALL",
"4 FDEF",
"4 ENDF",
"7 MDAP[0]",
"7 MDAP[1]",
"6 IUP[0]",
"6 IUP[1]",
"6 SHP[0]",
"6 SHP[1]",
"6 SHC[0]",
"6 SHC[1]",
"6 SHZ[0]",
"6 SHZ[1]",
"5 SHPIX",
"2 IP",
"8 MSIRP[0]",
"8 MSIRP[1]",
"7 AlignRP",
"4 RTDG",
"7 MIAP[0]",
"7 MIAP[1]",
"6 NPushB",
"6 NPushW",
"2 WS",
"2 RS",
"5 WCvtP",
"4 RCvt",
"5 GC[0]",
"5 GC[1]",
"4 SCFS",
"5 MD[0]",
"5 MD[1]",
"5 MPPEM",
"3 MPS",
"6 FlipON",
"7 FlipOFF",
"5 DEBUG",
"2 LT",
"4 LTEQ",
"2 GT",
"4 GTEQ",
"2 EQ",
"3 NEQ",
"3 ODD",
"4 EVEN",
"2 IF",
"3 EIF",
"3 AND",
"2 OR",
"3 NOT",
"7 DeltaP1",
"3 SDB",
"3 SDS",
"3 ADD",
"3 SUB",
"3 DIV",
"3 MUL",
"3 ABS",
"3 NEG",
"5 FLOOR",
"7 CEILING",
"8 ROUND[0]",
"8 ROUND[1]",
"8 ROUND[2]",
"8 ROUND[3]",
"9 NROUND[0]",
"9 NROUND[1]",
"9 NROUND[2]",
"9 NROUND[3]",
"5 WCvtF",
"7 DeltaP2",
"7 DeltaP3",
"A DeltaCn[0]",
"A DeltaCn[1]",
"A DeltaCn[2]",
"6 SROUND",
"8 S45Round",
"4 JROT",
"4 JROF",
"4 ROFF",
"7 INS_$7B",
"4 RUTG",
"4 RDTG",
"5 SANGW",
"2 AA",
"6 FlipPT",
"8 FlipRgON",
"9 FlipRgOFF",
"7 INS_$83",
"7 INS_$84",
"8 ScanCTRL",
"9 SDPvTL[0]",
"9 SDPvTL[1]",
"7 GetINFO",
"4 IDEF",
"4 ROLL",
"3 MAX",
"3 MIN",
"8 ScanTYPE",
"8 InstCTRL",
"7 INS_$8F",
"7 INS_$90",
"7 GXAXIS",
"7 INS_$92",
"7 INS_$93",
"7 INS_$94",
"7 INS_$95",
"7 INS_$96",
"7 INS_$97",
"7 INS_$98",
"7 INS_$99",
"7 INS_$9A",
"7 INS_$9B",
"7 INS_$9C",
"7 INS_$9D",
"7 INS_$9E",
"7 INS_$9F",
"7 INS_$A0",
"7 INS_$A1",
"7 INS_$A2",
"7 INS_$A3",
"7 INS_$A4",
"7 INS_$A5",
"7 INS_$A6",
"7 INS_$A7",
"7 INS_$A8",
"7 INS_$A9",
"7 INS_$AA",
"7 INS_$AB",
"7 INS_$AC",
"7 INS_$AD",
"7 INS_$AE",
"7 INS_$AF",
"8 PushB[0]",
"8 PushB[1]",
"8 PushB[2]",
"8 PushB[3]",
"8 PushB[4]",
"8 PushB[5]",
"8 PushB[6]",
"8 PushB[7]",
"8 PushW[0]",
"8 PushW[1]",
"8 PushW[2]",
"8 PushW[3]",
"8 PushW[4]",
"8 PushW[5]",
"8 PushW[6]",
"8 PushW[7]",
"7 MDRP[G]",
"7 MDRP[B]",
"7 MDRP[W]",
"7 MDRP[?]",
"8 MDRP[rG]",
"8 MDRP[rB]",
"8 MDRP[rW]",
"8 MDRP[r?]",
"8 MDRP[mG]",
"8 MDRP[mB]",
"8 MDRP[mW]",
"8 MDRP[m?]",
"9 MDRP[mrG]",
"9 MDRP[mrB]",
"9 MDRP[mrW]",
"9 MDRP[mr?]",
"8 MDRP[pG]",
"8 MDRP[pB]",
"8 MDRP[pW]",
"8 MDRP[p?]",
"9 MDRP[prG]",
"9 MDRP[prB]",
"9 MDRP[prW]",
"9 MDRP[pr?]",
"9 MDRP[pmG]",
"9 MDRP[pmB]",
"9 MDRP[pmW]",
"9 MDRP[pm?]",
"A MDRP[pmrG]",
"A MDRP[pmrB]",
"A MDRP[pmrW]",
"A MDRP[pmr?]",
"7 MIRP[G]",
"7 MIRP[B]",
"7 MIRP[W]",
"7 MIRP[?]",
"8 MIRP[rG]",
"8 MIRP[rB]",
"8 MIRP[rW]",
"8 MIRP[r?]",
"8 MIRP[mG]",
"8 MIRP[mB]",
"8 MIRP[mW]",
"8 MIRP[m?]",
"9 MIRP[mrG]",
"9 MIRP[mrB]",
"9 MIRP[mrW]",
"9 MIRP[mr?]",
"8 MIRP[pG]",
"8 MIRP[pB]",
"8 MIRP[pW]",
"8 MIRP[p?]",
"9 MIRP[prG]",
"9 MIRP[prB]",
"9 MIRP[prW]",
"9 MIRP[pr?]",
"9 MIRP[pmG]",
"9 MIRP[pmB]",
"9 MIRP[pmW]",
"9 MIRP[pm?]",
"A MIRP[pmrG]",
"A MIRP[pmrB]",
"A MIRP[pmrW]",
"A MIRP[pmr?]"
};
return opcode_names[opcode];
}
static void
sfnt_draw_debugger (struct sfnt_interpreter *interpreter)
{
int x, y, i;
char buffer[80];
const char *name;
int opcode;
sprintf (buffer, "opcode:IP:depth: 0x%x:%d:%d",
interpreter->instructions[interpreter->IP],
interpreter->IP,
interpreter->call_depth);
/* Clear the window. */
XFillRectangle (display, window, background_gc,
0, 0, 65535, 65535);
/* Draw some information about the opcode. */
XDrawString (display, window, point_gc, 0, 13, buffer,
strlen (buffer));
opcode = interpreter->instructions[interpreter->IP];
sprintf (buffer, "opcode: %s",
sfnt_name_instruction (opcode));
XDrawString (display, window, point_gc, 14, 27, buffer,
strlen (buffer));
if (interpreter->state.project
== sfnt_project_onto_x_axis_vector)
name = "X axis";
else if (interpreter->state.project
== sfnt_project_onto_y_axis_vector)
name = "Y axis";
else
name = "Any";
sprintf (buffer, "projection function: %s", name);
XDrawString (display, window, point_gc, 28, 42, buffer,
strlen (buffer));
/* Draw each point onto the window. */
for (i = 0; i < interpreter->glyph_zone->num_points; ++i)
{
x = interpreter->glyph_zone->x_current[i] / 16;
y = (200 - interpreter->glyph_zone->y_current[i] / 16);
XFillRectangle (display, window, point_gc, x, y, 4, 4);
}
}
static void
sfnt_run_hook (struct sfnt_interpreter *interpreter)
{
pid_t pid;
XEvent event;
#ifdef TEST_BREAK_AFTER
static unsigned int instructions;
if (++instructions < TEST_BREAK_AFTER)
return;
#endif
pid = fork ();
if (pid == 0)
{
sfnt_setup_debugger ();
while (true)
{
XNextEvent (display, &event);
switch (event.type)
{
case KeyPress:
XDestroyWindow (display, window);
XCloseDisplay (display);
exit (0);
break;
case Expose:
sfnt_draw_debugger (interpreter);
break;
}
}
}
else
{
while (waitpid (pid, NULL, 0) != pid && errno == EINTR)
/* Spin. */;
}
}
static struct sfnt_prep_table *exec_prep;
static struct sfnt_fpgm_table *exec_fpgm;
static const char *
sfnt_identify_instruction (struct sfnt_interpreter *interpreter)
{
static char buffer[256];
unsigned char *where;
where = interpreter->instructions + interpreter->IP;
if (exec_prep
&& where >= exec_prep->instructions
&& where < (exec_prep->instructions
+ exec_prep->num_instructions))
{
sprintf (buffer, "prep+%td",
where - exec_prep->instructions);
return buffer;
}
if (exec_fpgm
&& exec_fpgm->instructions
&& where >= exec_fpgm->instructions
&& where < (exec_fpgm->instructions
+ exec_fpgm->num_instructions))
{
sprintf (buffer, "fpgm+%td",
where - exec_fpgm->instructions);
return buffer;
}
sprintf (buffer, "IP+%td", where - interpreter->instructions);
return buffer;
}
/* Function called to rasterize a glyph outline. */
#define TYPE struct sfnt_glyph_outline *
static struct sfnt_raster *(*test_raster_glyph_outline) (TYPE);
#undef TYPE
static void
sfnt_verbose (struct sfnt_interpreter *interpreter)
{
struct sfnt_instructed_outline temp;
struct sfnt_glyph_outline *outline;
struct sfnt_raster *raster;
unsigned char opcode;
const char *name;
static unsigned int instructions;
sfnt_fixed advance;
/* Build a temporary outline containing the values of the
interpreter's glyph zone. */
if (interpreter->glyph_zone)
{
temp.num_points = interpreter->glyph_zone->num_points;
temp.num_contours = interpreter->glyph_zone->num_contours;
temp.contour_end_points = interpreter->glyph_zone->contour_end_points;
temp.x_points = interpreter->glyph_zone->x_current;
temp.y_points = interpreter->glyph_zone->y_current;
temp.flags = interpreter->glyph_zone->flags;
outline = sfnt_build_instructed_outline (&temp, &advance);
if (!outline)
return;
printf ("outline bounds: %g %g, %g %g\n",
sfnt_coerce_fixed (outline->xmin),
sfnt_coerce_fixed (outline->ymin),
sfnt_coerce_fixed (outline->xmax),
sfnt_coerce_fixed (outline->ymax));
raster = (*test_raster_glyph_outline) (outline);
if (raster)
sfnt_test_raster (raster, NULL, 0);
xfree (outline);
xfree (raster);
}
opcode = interpreter->instructions[interpreter->IP];
printf ("opcode, number of instructions: %s %u\n",
sfnt_name_instruction (opcode), instructions++);
printf ("instruction: %s\n",
sfnt_identify_instruction (interpreter));
if (interpreter->state.project
== sfnt_project_onto_x_axis_vector)
name = "X axis";
else if (interpreter->state.project
== sfnt_project_onto_y_axis_vector)
name = "Y axis";
else
name = "Any";
printf ("projection function: %s\n", name);
printf ("proj and free vecs: %d %d %d %d\n",
interpreter->state.projection_vector.x,
interpreter->state.projection_vector.y,
interpreter->state.freedom_vector.x,
interpreter->state.freedom_vector.y);
}
static void
sfnt_push_hook (struct sfnt_interpreter *interpreter,
uint32_t value)
{
int32_t alternate;
alternate = value;
fprintf (stderr, "--> %"PRIi32"\n", alternate);
}
static void
sfnt_pop_hook (struct sfnt_interpreter *interpreter,
uint32_t value)
{
int32_t alternate;
alternate = value;
fprintf (stderr, "<<- %"PRIi32"\n", alternate);
}
static void
sfnt_test_uvs (int fd, struct sfnt_cmap_format_14 *format14)
{
struct sfnt_uvs_context *context;
size_t i, j, k;
sfnt_glyph glyph;
sfnt_char c;
struct sfnt_nondefault_uvs_table *uvs;
struct sfnt_default_uvs_table *default_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].default_uvs)
{
default_uvs = context->records[i].default_uvs;
for (j = 0; j < default_uvs->num_unicode_value_ranges; ++j)
{
fprintf (stderr, " Default UVS: %u, %u\n",
default_uvs->ranges[j].start_unicode_value,
default_uvs->ranges[j].additional_count);
c = default_uvs->ranges[j].start_unicode_value;
k = 0;
for (; k <= default_uvs->ranges[j].additional_count; ++k)
{
if (!sfnt_is_character_default (default_uvs, c + k))
abort ();
}
}
}
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
time you are reading this, they probably no longer work.
Compile like so in this directory:
gcc -Demacs -I. -I. -I../lib -I../lib -MMD -MF deps/.d -MP
-fno-common -Wall -Warith-conversion -Wdate-time
-Wdisabled-optimization -Wdouble-promotion -Wduplicated-cond
-Wextra -Wformat-signedness -Winit-self -Winvalid-pch -Wlogical-op
-Wmissing-declarations -Wmissing-include-dirs -Wmissing-prototypes
-Wnested-externs -Wnull-dereference -Wold-style-definition
-Wopenmp-simd -Wpacked -Wpointer-arith -Wstrict-prototypes
-Wsuggest-attribute=format -Wsuggest-final-methods
-Wsuggest-final-types -Wtrampolines -Wuninitialized
-Wunknown-pragmas -Wunused-macros -Wvariadic-macros
-Wvector-operation-performance -Wwrite-strings -Warray-bounds=2
-Wattribute-alias=2 -Wformat=2 -Wformat-truncation=2
-Wimplicit-fallthrough=5 -Wshift-overflow=2 -Wuse-after-free=3
-Wvla-larger-than=4031 -Wredundant-decls
-Wno-missing-field-initializers -Wno-override-init
-Wno-sign-compare -Wno-type-limits -Wno-unused-parameter
-Wno-format-nonliteral -Wno-bidi-chars -g3 -O0 -DTEST sfnt.c -o
sfnt ../lib/libgnu.a -lX11 -lXrender
after gnulib has been built. Then, run ./sfnt
/path/to/font.ttf. */
int
main (int argc, char **argv)
{
struct sfnt_offset_subtable *font;
struct sfnt_cmap_encoding_subtable *subtables;
struct sfnt_cmap_encoding_subtable_data **data;
struct sfnt_cmap_table *table;
int fd, i, j;
sfnt_char character;
struct sfnt_head_table *head;
struct sfnt_hhea_table *hhea;
struct sfnt_loca_table_short *loca_short;
struct sfnt_loca_table_long *loca_long;
struct sfnt_glyf_table *glyf;
struct sfnt_glyph *glyph;
sfnt_glyph code;
struct sfnt_test_dcontext dcontext;
struct sfnt_glyph_outline *outline;
struct timespec start, end, sub, sub1, sub2, sub3;
static struct sfnt_maxp_table *maxp;
struct sfnt_raster *raster;
struct sfnt_hmtx_table *hmtx;
struct sfnt_glyph_metrics metrics;
struct sfnt_name_table *name;
unsigned char *string;
struct sfnt_name_record record;
struct sfnt_meta_table *meta;
struct sfnt_ttc_header *ttc;
struct sfnt_interpreter *interpreter;
struct sfnt_cvt_table *cvt;
struct sfnt_fpgm_table *fpgm;
const char *trap;
struct sfnt_prep_table *prep;
struct sfnt_graphics_state state;
struct sfnt_instructed_outline *value;
struct sfnt_fvar_table *fvar;
struct sfnt_gvar_table *gvar;
struct sfnt_avar_table *avar;
struct sfnt_cvar_table *cvar;
struct sfnt_OS_2_table *OS_2;
struct sfnt_post_table *post;
sfnt_fixed scale;
char *fancy;
int *advances;
struct sfnt_raster **rasters;
size_t length;
char *axis_name;
struct sfnt_instance *instance;
struct sfnt_blend blend;
struct sfnt_metrics_distortion distortion;
sfnt_fixed advance;
if (argc < 2)
return 1;
instance = NULL;
if (!strcmp (argv[1], "--check-interpreter"))
{
interpreter = sfnt_make_test_interpreter ();
if (!interpreter)
abort ();
if (getenv ("SFNT_VERBOSE"))
{
interpreter->run_hook = sfnt_verbose;
interpreter->push_hook = sfnt_push_hook;
interpreter->pop_hook = sfnt_pop_hook;
}
for (i = 0; i < ARRAYELTS (all_tests); ++i)
sfnt_run_interpreter_test (&all_tests[i], interpreter);
exit (0);
}
if (getenv ("SFNT_EXACT_SCALING"))
test_raster_glyph_outline = sfnt_raster_glyph_outline_exact;
else
test_raster_glyph_outline = sfnt_raster_glyph_outline;
fd = open (argv[1], O_RDONLY);
if (fd < 0)
return 1;
ttc = NULL;
font = sfnt_read_table_directory (fd);
if (font == (struct sfnt_offset_subtable *) -1)
{
if (lseek (fd, 0, SEEK_SET) != 0)
return 1;
ttc = sfnt_read_ttc_header (fd);
if (!ttc)
return 1;
fprintf (stderr, "TrueType collection: %"PRIu32" fonts installed\n",
ttc->num_fonts);
fflush (stderr);
printf ("Which font? ");
if (scanf ("%d", &i) == EOF)
return 1;
if (i >= ttc->num_fonts || i < 0)
{
printf ("out of range\n");
return 1;
}
if (lseek (fd, ttc->offset_table[i], SEEK_SET)
!= ttc->offset_table[i])
return 1;
font = sfnt_read_table_directory (fd);
}
if (!font || font == (struct sfnt_offset_subtable *) -1)
{
close (fd);
return 1;
}
for (i = 0; i < font->num_tables; ++i)
fprintf (stderr, "Found new subtable with tag %"PRIx32
" at offset %"PRIu32"\n",
font->subtables[i].tag,
font->subtables[i].offset);
table = sfnt_read_cmap_table (fd, font, &subtables, &data);
if (!table)
{
close (fd);
xfree (font);
return 1;
}
fprintf (stderr, "number of subtables: %"PRIu16"\n",
table->num_subtables);
for (i = 0; i < table->num_subtables; ++i)
{
fprintf (stderr, "Found cmap table %"PRIu32": %p\n",
subtables[i].offset, (void *) data[i]);
if (data[i])
fprintf (stderr, " format: %"PRIu16"\n",
data[i]->format);
}
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 18
#define EASY_PPEM 18
interpreter = NULL;
head = sfnt_read_head_table (fd, font);
hhea = sfnt_read_hhea_table (fd, font);
glyf = sfnt_read_glyf_table (fd, font);
maxp = sfnt_read_maxp_table (fd, font);
name = sfnt_read_name_table (fd, font);
meta = sfnt_read_meta_table (fd, font);
cvt = sfnt_read_cvt_table (fd, font);
fpgm = sfnt_read_fpgm_table (fd, font);
prep = sfnt_read_prep_table (fd, font);
fvar = sfnt_read_fvar_table (fd, font);
gvar = sfnt_read_gvar_table (fd, font);
avar = sfnt_read_avar_table (fd, font);
OS_2 = sfnt_read_OS_2_table (fd, font);
post = sfnt_read_post_table (fd, font);
cvar = NULL;
hmtx = NULL;
if (fvar && cvt)
cvar = sfnt_read_cvar_table (fd, font, fvar, cvt);
if (cvar)
fprintf (stderr, "cvar table found\n");
exec_prep = prep;
exec_fpgm = fpgm;
fancy = getenv ("SFNT_FANCY_TEST");
loca_long = NULL;
loca_short = NULL;
if (OS_2)
fprintf (stderr, "OS/2 table found!\nach_vendor_id: %.4s\n",
OS_2->ach_vendor_id);
if (post)
fprintf (stderr, "post table: format: %g; italic-angle: %g;\n"
"underline_position: %"PRIi16"; underline_thickness: %"
PRIi16";\n"
"is_fixed_pitch: %"PRIu32"\n",
sfnt_coerce_fixed (post->format),
sfnt_coerce_fixed (post->italic_angle),
post->underline_position,
post->underline_thickness,
post->is_fixed_pitch);
if (fvar)
{
fprintf (stderr, "FVAR table found!\n"
"version: %"PRIu16".%"PRIu16"\n"
"axis_count: %"PRIu16"\n"
"axis_size: %"PRIu16"\n"
"instance_count: %"PRIu16"\n"
"instance_size: %"PRIu16"\n",
fvar->major_version,
fvar->minor_version,
fvar->axis_count,
fvar->axis_size,
fvar->instance_count,
fvar->instance_size);
for (i = 0; i < fvar->axis_count; ++i)
{
if (name)
{
axis_name
= (char *) sfnt_find_name (name, fvar->axis[i].name_id,
&record);
if (axis_name)
fprintf (stderr, "axis no: %d; name: %.*s\n",
i, record.length, axis_name);
}
fprintf (stderr, " axis: %"PRIx32" %g %g %g\n",
fvar->axis[i].axis_tag,
sfnt_coerce_fixed (fvar->axis[i].min_value),
sfnt_coerce_fixed (fvar->axis[i].default_value),
sfnt_coerce_fixed (fvar->axis[i].max_value));
}
for (i = 0; i < fvar->instance_count; ++i)
{
if (name)
{
axis_name
= (char *) sfnt_find_name (name, fvar->instance[i].name_id,
&record);
if (axis_name)
fprintf (stderr, "instance no: %d; name: %.*s\n",
i, record.length, axis_name);
}
}
if (fvar->instance_count > 1)
{
printf ("instance? ");
if (scanf ("%d", &i) == EOF)
goto free_lab;
if (i >= fvar->instance_count)
goto free_lab;
if (i >= 0)
instance = &fvar->instance[i];
}
}
if (gvar)
fprintf (stderr, "gvar table found\n");
if (avar)
{
fprintf (stderr, "avar table found\n");
for (i = 0; i < avar->axis_count; ++i)
{
fprintf (stderr, "axis: %d, %"PRIu16" pairs\n",
i, avar->segments[i].pair_count);
for (j = 0; j < avar->segments[i].pair_count; ++j)
fprintf (stderr, "pair: %g, %g\n",
(avar->segments[i].correspondence[j].from_coord
/ 16384.0),
(avar->segments[i].correspondence[j].to_coord
/ 16384.0));
}
}
memset (&blend, 0, sizeof blend);
if (instance && gvar)
{
sfnt_init_blend (&blend, fvar, gvar, avar,
cvar);
for (i = 0; i < fvar->axis_count; ++i)
blend.coords[i] = instance->coords[i];
sfnt_normalize_blend (&blend);
}
if (fancy)
{
length = strlen (fancy);
scale = sfnt_div_fixed (FANCY_PPEM, head->units_per_em);
if (hhea && maxp)
hmtx = sfnt_read_hmtx_table (fd, font, hhea, maxp);
if (!maxp || !head || !prep || !hmtx || !hhea
|| table->num_subtables < 1)
exit (1);
if (head->index_to_loc_format)
{
loca_long = sfnt_read_loca_table_long (fd, font);
if (!loca_long)
return 1;
fprintf (stderr, "long loca table has %zu glyphs\n",
loca_long->num_offsets);
}
else
{
loca_short = sfnt_read_loca_table_short (fd, font);
if (!loca_short)
return 1;
fprintf (stderr, "short loca table has %zu glyphs\n",
loca_short->num_offsets);
}
interpreter = sfnt_make_interpreter (maxp, cvt, head, fvar,
FANCY_PPEM, FANCY_PPEM);
if (instance && gvar)
sfnt_vary_interpreter (interpreter, &blend);
if (!interpreter)
exit (1);
if (fpgm)
{
fprintf (stderr, "interpreting the font program, with"
" %zu instructions\n", fpgm->num_instructions);
trap = sfnt_interpret_font_program (interpreter, fpgm);
if (trap)
fprintf (stderr, "**TRAP**: %s\n", trap);
}
if (prep)
{
fprintf (stderr, "interpreting the control value program, with"
" %zu instructions\n", prep->num_instructions);
trap = sfnt_interpret_control_value_program (interpreter, prep,
&state);
if (trap)
fprintf (stderr, "**TRAP**: %s\n", trap);
}
state = interpreter->state;
advances = alloca (sizeof *advances * length);
rasters = alloca (sizeof *rasters * length);
for (i = 0; i < length; ++i)
{
code = sfnt_lookup_glyph (fancy[i], data[0]);
if (!code)
exit (2);
glyph = sfnt_read_glyph (code, glyf, loca_short,
loca_long);
if (!glyph || !glyph->simple)
exit (3);
if (instance && gvar)
sfnt_vary_simple_glyph (&blend, code, glyph,
&distortion);
else
memset (&distortion, 0, sizeof distortion);
if (sfnt_lookup_glyph_metrics (code, &metrics,
hmtx, hhea, maxp))
exit (4);
interpreter->state = state;
trap = sfnt_interpret_simple_glyph (glyph, interpreter,
&metrics, &value);
if (trap)
{
fprintf (stderr, "*TRAP*: %s\n", trap);
exit (5);
}
outline = sfnt_build_instructed_outline (value, &advance);
advances[i] = (advance / 65536);
fprintf (stderr, "advance: %d\n", advances[i]);
if (!outline)
exit (6);
xfree (value);
raster = (*test_raster_glyph_outline) (outline);
if (!raster)
exit (7);
xfree (outline);
rasters[i] = raster;
}
sfnt_x_raster (rasters, advances, length, hhea, scale);
exit (0);
}
if (hhea && maxp)
hmtx = sfnt_read_hmtx_table (fd, font, hhea, maxp);
if (maxp)
fprintf (stderr, "maxp says num glyphs is %"PRIu16"\n",
maxp->num_glyphs);
if (name)
{
fprintf (stderr, "name table of format: %"PRIu16" count: %"
PRIu16"\n", name->format, name->count);
string = sfnt_find_name (name, SFNT_NAME_FONT_FAMILY,
&record);
if (string)
fprintf (stderr, "FONT_FAMILY: %"PRIu16", %"PRIu16"\n",
record.platform_id, record.length);
}
if (meta)
{
fprintf (stderr, "meta table with count: %"PRIu32"\n",
meta->num_data_maps);
for (i = 0; i < meta->num_data_maps; ++i)
fprintf (stderr, " meta tag: %"PRIx32"\n",
meta->data_maps[i].tag);
}
loca_long = NULL;
loca_short = NULL;
if (head)
{
fprintf (stderr, "HEAD table:\n"
"version: \t\t\t%g\n"
"revision: \t\t\t%g\n"
"checksum_adjustment: \t\t%"PRIu32"\n"
"magic: \t\t\t\t%"PRIx32"\n"
"flags: \t\t\t\t%"PRIx16"\n"
"units_per_em: \t\t\t%"PRIu16"\n"
"xmin, ymin, xmax, ymax: \t%d, %d, %d, %d\n"
"mac_style: \t\t\t%"PRIx16"\n"
"lowest_rec_ppem: \t\t%"PRIu16"\n"
"font_direction_hint: \t\t%"PRIi16"\n"
"index_to_loc_format: \t\t%"PRIi16"\n"
"glyph_data_format: \t\t%"PRIi16"\n",
sfnt_coerce_fixed (head->version),
sfnt_coerce_fixed (head->revision),
head->checksum_adjustment,
head->magic,
head->flags,
head->units_per_em,
(int) head->xmin,
(int) head->ymin,
(int) head->xmax,
(int) head->ymax,
head->mac_style,
head->lowest_rec_ppem,
head->font_direction_hint,
head->index_to_loc_format,
head->glyph_data_format);
if (head->index_to_loc_format)
{
loca_long = sfnt_read_loca_table_long (fd, font);
if (!loca_long)
return 1;
fprintf (stderr, "long loca table has %zu glyphs\n",
loca_long->num_offsets);
}
else
{
loca_short = sfnt_read_loca_table_short (fd, font);
if (!loca_short)
return 1;
fprintf (stderr, "short loca table has %zu glyphs\n",
loca_short->num_offsets);
}
}
if (hhea)
fprintf (stderr, "HHEA table:\n"
"version: \t\t\t%g\n"
"ascent, descent: \t\t%d %d\n"
"line_gap: \t\t\t%d\n"
"advance_width_max: \t\t%u\n"
"min_lsb: \t\t\t%d\n"
"min_rsb: \t\t\t%d\n"
"caret_srise: \t\t\t%d\n"
"caret_srun: \t\t\t%d\n",
sfnt_coerce_fixed (hhea->version),
(int) hhea->ascent,
(int) hhea->descent,
(int) hhea->line_gap,
(unsigned int) hhea->advance_width_max,
(int) hhea->min_left_side_bearing,
(int) hhea->min_right_side_bearing,
(int) hhea->caret_slope_rise,
(int) hhea->caret_slope_run);
if (head && maxp && maxp->version >= 0x00010000)
{
fprintf (stderr, "creating interpreter\n"
"the size of the stack is %"PRIu16"\n"
"the size of the twilight zone is %"PRIu16"\n"
"the size of the storage area is %"PRIu16"\n"
"there are at most %"PRIu16" idefs\n"
"there are at most %"PRIu16" fdefs\n"
"the cvt is %zu fwords in length\n",
maxp->max_stack_elements,
maxp->max_twilight_points,
maxp->max_storage,
maxp->max_instruction_defs,
maxp->max_function_defs,
cvt ? cvt->num_elements : 0ul);
interpreter = sfnt_make_interpreter (maxp, cvt, head,
fvar, FANCY_PPEM,
FANCY_PPEM);
if (getenv ("SFNT_DEBUG"))
interpreter->run_hook = sfnt_run_hook;
else if (getenv ("SFNT_VERBOSE"))
{
interpreter->run_hook = sfnt_verbose;
interpreter->push_hook = sfnt_push_hook;
interpreter->pop_hook = sfnt_pop_hook;
}
state = interpreter->state;
if (instance && gvar)
sfnt_vary_interpreter (interpreter, &blend);
if (fpgm)
{
fprintf (stderr, "interpreting the font program, with"
" %zu instructions\n", fpgm->num_instructions);
trap = sfnt_interpret_font_program (interpreter, fpgm);
if (trap)
fprintf (stderr, "**TRAP**: %s\n", trap);
}
if (prep)
{
fprintf (stderr, "interpreting the control value program, with"
" %zu instructions\n", prep->num_instructions);
trap = sfnt_interpret_control_value_program (interpreter, prep,
&state);
if (trap)
fprintf (stderr, "**TRAP**: %s\n", trap);
}
}
while (true)
{
printf ("table, character? ");
if (scanf ("%d %"SCNu32"", &i, &character) == EOF)
break;
if (i < 0 || i >= table->num_subtables)
{
printf ("table out of range\n");
continue;
}
if (!data[i])
{
printf ("table not present\n");
continue;
}
code = sfnt_lookup_glyph (character, data[i]);
printf ("glyph is %"PRIu32"\n", code);
if ((loca_long || loca_short) && glyf)
{
scale = sfnt_div_fixed (EASY_PPEM, head->units_per_em);
glyph = sfnt_read_glyph (code, glyf, loca_short,
loca_long);
if (glyph)
{
printf ("glyph is: %s\n",
glyph->simple ? "simple" : "compound");
dcontext.glyf = glyf;
dcontext.loca_short = loca_short;
dcontext.loca_long = loca_long;
dcontext.hmtx = hmtx;
dcontext.hhea = hhea;
dcontext.maxp = maxp;
if (instance && gvar)
dcontext.blend = &blend;
else
dcontext.blend = NULL;
if (glyph->simple && instance && gvar)
{
printf ("applying variations to simple glyph...\n");
clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start);
if (sfnt_vary_simple_glyph (&blend, code, glyph,
&distortion))
printf ("variation failed!\n");
clock_gettime (CLOCK_THREAD_CPUTIME_ID, &end);
sub = timespec_sub (end, start);
printf ("time spent varying: %lld sec %ld nsec\n",
(long long) sub.tv_sec, sub.tv_nsec);
printf ("distortions: %"PRIi16", %"PRIi16"\n",
distortion.origin, distortion.advance);
}
else if (instance && gvar)
{
printf ("applying variations to compound glyph...\n");
if (sfnt_vary_compound_glyph (&blend, code, glyph,
&distortion))
printf ("variation failed!\n");
}
if (sfnt_lookup_glyph_metrics (code, &metrics,
hmtx, hhea, maxp))
{
printf ("metrics lookup failure");
memset (&metrics, 0, sizeof metrics);
}
if (sfnt_decompose_glyph (glyph, &metrics,
sfnt_test_move_to,
sfnt_test_line_to,
sfnt_test_curve_to,
sfnt_test_get_glyph,
sfnt_test_free_glyph,
sfnt_test_get_metrics,
&dcontext))
printf ("decomposition failure\n");
if (sfnt_lookup_glyph_metrics (code, &metrics,
hmtx, hhea, maxp))
{
printf ("metrics lookup failure");
memset (&metrics, 0, sizeof metrics);
}
/* Time this important bit. */
clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start);
outline = sfnt_build_glyph_outline (glyph, scale,
&metrics,
sfnt_test_get_glyph,
sfnt_test_free_glyph,
sfnt_test_get_metrics,
&dcontext);
clock_gettime (CLOCK_THREAD_CPUTIME_ID, &end);
sub = timespec_sub (end, start);
memset (&sub1, 0, sizeof sub1);
if (outline)
{
fprintf (stderr, "outline origin, rbearing: %"
PRIi32" %"PRIi32"\n",
outline->origin,
outline->xmax - outline->origin);
sfnt_test_max = outline->ymax - outline->ymin;
for (i = 0; i < outline->outline_used; i++)
printf ("ctx.%s (%g, %g) /* %g, %g */\n",
((outline->outline[i].flags
& SFNT_GLYPH_OUTLINE_LINETO)
? "lineTo" : "moveTo"),
sfnt_coerce_fixed (outline->outline[i].x
- outline->xmin),
sfnt_coerce_fixed (sfnt_test_max
- (outline->outline[i].y
- outline->ymin)),
sfnt_coerce_fixed (outline->outline[i].x
- outline->xmin),
sfnt_coerce_fixed (outline->outline[i].y
- outline->ymin));
clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start);
sfnt_build_outline_edges (outline, sfnt_test_edge_ignore,
NULL);
clock_gettime (CLOCK_THREAD_CPUTIME_ID, &end);
sub1 = timespec_sub (end, start);
sfnt_build_outline_edges (outline, sfnt_test_edge,
NULL);
raster = NULL;
clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start);
for (i = 0; i < 12800; ++i)
{
xfree (raster);
raster = (*test_raster_glyph_outline) (outline);
}
clock_gettime (CLOCK_THREAD_CPUTIME_ID, &end);
sub2 = timespec_sub (end, start);
/* Print out the raster. */
sfnt_test_raster (raster, hhea, scale);
printf ("raster offsets: %d, %d\n",
raster->offx, raster->offy);
xfree (raster);
printf ("outline bounds: %g %g, %g %g\n",
sfnt_coerce_fixed (outline->xmin),
sfnt_coerce_fixed (outline->ymin),
sfnt_coerce_fixed (outline->xmax),
sfnt_coerce_fixed (outline->ymax));
}
if (hmtx && head)
{
sfnt_scale_metrics (&metrics, scale);
printf ("scaled lbearing, advance: %g, %g\n",
sfnt_coerce_fixed (metrics.lbearing),
sfnt_coerce_fixed (metrics.advance));
if (!sfnt_lookup_glyph_metrics (code, &metrics, hmtx,
hhea, maxp))
{
sfnt_scale_metrics (&metrics, scale);
printf ("lbearing, advance: %g, %g\n",
sfnt_coerce_fixed (metrics.lbearing),
sfnt_coerce_fixed (metrics.advance));
}
if (interpreter)
{
if (!sfnt_lookup_glyph_metrics (code, &metrics,
hmtx, hhea, maxp))
{
printf ("interpreting glyph\n");
interpreter->state = state;
clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start);
if (glyph->simple)
trap
= sfnt_interpret_simple_glyph (glyph,
interpreter,
&metrics,
&value);
else
#define GG sfnt_test_get_glyph
#define FG sfnt_test_free_glyph
trap
= sfnt_interpret_compound_glyph (glyph,
interpreter,
&state,
GG, FG,
hmtx, hhea,
maxp,
&metrics,
&dcontext,
&value);
#undef GG
#undef FG
clock_gettime (CLOCK_THREAD_CPUTIME_ID, &end);
sub3 = timespec_sub (end, start);
if (trap)
printf ("**TRAP**: %s\n", trap);
else
{
printf ("rasterizing instructed outline\n");
if (outline)
xfree (outline);
outline
= sfnt_build_instructed_outline (value,
&advance);
xfree (value);
#define LB outline->xmin - outline->origin
#define RB outline->xmax - outline->origin
printf ("instructed advance, lb, rb: %g %g %g\n",
sfnt_coerce_fixed (advance),
sfnt_coerce_fixed (LB),
sfnt_coerce_fixed (RB));
#undef LB
#undef RB
if (outline)
{
raster
= (*test_raster_glyph_outline) (outline);
if (raster)
{
sfnt_test_raster (raster, hhea, scale);
printf ("raster offsets: %d, %d\n",
raster->offx, raster->offy);
xfree (raster);
}
}
}
fprintf (stderr, "execution time: %lld sec %ld nse"
"c\n",
(long long) sub3.tv_sec, sub3.tv_nsec);
}
interpreter->run_hook = NULL;
}
}
printf ("time spent outlining: %lld sec %ld nsec\n",
(long long) sub.tv_sec, sub.tv_nsec);
printf ("time spent building edges: %lld sec %ld nsec\n",
(long long) sub1.tv_sec, sub1.tv_nsec);
printf ("time spent rasterizing: %lld sec %ld nsec\n",
(long long) sub2.tv_sec / 12800,
sub2.tv_nsec / 12800);
xfree (outline);
}
sfnt_free_glyph (glyph);
}
}
free_lab:
xfree (font);
for (i = 0; i < table->num_subtables; ++i)
xfree (data[i]);
if (instance && gvar)
sfnt_free_blend (&blend);
xfree (table);
xfree (data);
xfree (subtables);
xfree (head);
xfree (hhea);
xfree (loca_long);
xfree (loca_short);
xfree (glyf);
xfree (maxp);
xfree (hmtx);
xfree (name);
xfree (meta);
xfree (ttc);
xfree (cvt);
xfree (fpgm);
xfree (interpreter);
xfree (prep);
xfree (fvar);
xfree (gvar);
xfree (avar);
xfree (cvar);
xfree (OS_2);
xfree (post);
return 0;
}
#endif