[SFNT_TABLE_FVAR] = 0x66766172,
[SFNT_TABLE_GVAR] = 0x67766172,
[SFNT_TABLE_CVAR] = 0x63766172,
+ [SFNT_TABLE_AVAR] = 0x61766172,
};
/* Swap values from TrueType to system byte order. */
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)
given pixel size. Return the outline data with a refcount of 0
upon success, or NULL upon failure.
+ Use the scaled glyph METRICS to determine the origin point of the
+ outline.
+
Call GET_GLYPH and FREE_GLYPH with the specified DCONTEXT to obtain
glyphs for compound glyph subcomponents.
sfnt_build_glyph_outline (struct sfnt_glyph *glyph,
struct sfnt_head_table *head,
int pixel_size,
+ struct sfnt_glyph_metrics *metrics,
sfnt_get_glyph_proc get_glyph,
sfnt_free_glyph_proc free_glyph,
void *dcontext)
{
struct sfnt_glyph_outline *outline;
int rc;
+ sfnt_fword origin;
memset (&build_outline_context, 0, sizeof build_outline_context);
return NULL;
}
+ /* Compute the origin position. */
+ origin = outline->xmin - metrics->lbearing;
+ outline->origin
+ = (origin + sfnt_mul_fixed (glyph->origin_distortion,
+ build_outline_context.factor));
+
return outline;
}
+ (SFNT_POLY_ALIGNMENT - 1))
& ~(SFNT_POLY_ALIGNMENT - 1));
- raster->offx = sfnt_floor_fixed (outline->xmin) >> 16;
+ /* 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) >> 16;
raster->offy = sfnt_floor_fixed (outline->ymin) >> 16;
}
= sfnt_mul_fixed (metrics->advance * 65536, factor);
}
+/* Like `sfnt_scale_metrics', except it scales the specified metrics
+ by a factor calculated using the given PPEM and HEAD table's UPEM
+ value. */
+
+MAYBE_UNUSED TEST_STATIC void
+sfnt_scale_metrics_to_pixel_size (struct sfnt_glyph_metrics *metrics,
+ int ppem,
+ struct sfnt_head_table *head)
+{
+ sfnt_fixed factor;
+
+ /* Now calculate the factor scale lbearing and advance up to the
+ given PPEM size. */
+ factor = sfnt_div_fixed (ppem, head->units_per_em);
+ sfnt_scale_metrics (metrics, factor);
+}
+
\f
/* Font style parsing. */
\f
-#ifdef SFNT_ENABLE_HINTING
-
/* TrueType hinting support.
If you do not read the code in this section in conjunction with
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)
+ outline->origin
+ = instructed->x_points[instructed->num_points - 2];
+ else
+ outline->origin = 0;
+
if (rc)
{
xfree (outline);
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. */
*x1 = sfnt_mul_f26dot6_fixed (f1 * 64, scale);
*x2 = sfnt_mul_f26dot6_fixed (f2 * 64, scale);
return NULL;
}
-#endif /* SFNT_ENABLE_HINTING */
\f
#endif /* !TEST */
-#ifdef TEST
-
\f
/* Glyph variations. Instead of defining separate fonts for each
variation), `gvar' (glyph variation) and `cvar' (CVT variation)
tables in a font file. */
-struct sfnt_variation_axis
-{
- /* The axis tag. */
- uint32_t axis_tag;
-
- /* The minimum style coordinate for the axis. */
- sfnt_fixed min_value;
-
- /* The default style coordinate for the axis. */
- sfnt_fixed default_value;
-
- /* The maximum style coordinate for the axis. */
- sfnt_fixed max_value;
-
- /* Set to zero. */
- uint16_t flags;
-
- /* Identifier under which this axis's name will be found in the
- `name' table. */
- uint16_t name_id;
-};
-
-struct sfnt_instance
-{
- /* The instance name ID. */
- uint16_t name_id;
-
- /* Flags. */
- uint16_t flags;
-
- /* Optional PostScript name. */
- uint16_t ps_name_id;
-
- /* Coordinates of each defined instance. */
- sfnt_fixed *coords;
-};
-
-struct sfnt_fvar_table
-{
- /* Major version; should be 1. */
- uint16_t major_version;
-
- /* Minor version; should be 0. */
- uint16_t minor_version;
-
- /* Offset in bytes from the beginning of the table to the beginning
- of the first axis data. */
- uint16_t offset_to_data;
-
- /* Reserved field; always 2. */
- uint16_t count_size_pairs;
-
- /* Number of style axes in this font. */
- uint16_t axis_count;
-
- /* The number of bytes in each variation axis record. Currently 20
- bytes. */
- uint16_t axis_size;
-
- /* The number of named instances for the font found in the
- instance array. */
- uint16_t instance_count;
-
- /* The size of each instance record. */
- uint16_t instance_size;
-
- /* Variable length data. */
- struct sfnt_variation_axis *axis;
- struct sfnt_instance *instance;
-};
-
/* Read an fvar table from the given font FD. Use the table directory
specified in SUBTABLE.
Return the fvar table upon success, else NULL. */
-static struct sfnt_fvar_table *
+TEST_STATIC struct sfnt_fvar_table *
sfnt_read_fvar_table (int fd, struct sfnt_offset_subtable *subtable)
{
struct sfnt_table_directory *directory;
\f
-struct sfnt_gvar_table
-{
- /* Version of the glyph variations table. */
- uint16_t version;
-
- /* Reserved, currently 0. */
- uint16_t reserved;
-
- /* The number of style axes for this font. This must be the same
- number as axisCount in the 'fvar' table. */
- uint16_t axis_count;
-
- /* The number of shared coordinates. */
- uint16_t shared_coord_count;
-
- /* Byte offset from the beginning of this table to the list of
- shared style coordinates. */
- uint32_t offset_to_coord;
-
- /* The number of glyphs in this font; this should match the number
- of the glyphs store elsewhere in the font. */
- uint16_t glyph_count;
-
- /* Bit-field that gives the format of the offset array that
- follows. If the flag is 0, the type is uint16. If the flag is 1,
- the type is unit 32. */
- uint16_t flags;
-
- /* Byte offset from the beginning of this table to the first glyph
- glyphVariationData. */
- uint32_t offset_to_data;
-
- /* Number of bytes in the glyph variation data. */
- size_t data_size;
-
- /* Byte offsets from the beginning of the glyphVariationData array
- to the glyphVariationData for each glyph in the font. The format
- of this field is set by the flags field. */
- union {
- uint16_t *offset_word;
- uint32_t *offset_long;
- } u;
-
- /* Other variable length data. */
- sfnt_f2dot14 *global_coords;
- unsigned char *glyph_variation_data;
-};
-
/* Read a gvar table from the given font FD. Use the table directory
specified in SUBTABLE.
Return the gvar table upon success, else NULL. */
-static struct sfnt_gvar_table *
+TEST_STATIC struct sfnt_gvar_table *
sfnt_read_gvar_table (int fd, struct sfnt_offset_subtable *subtable)
{
struct sfnt_table_directory *directory;
\f
-/* Structure repesenting a set of axis coordinates and their
- normalized equivalents.
+/* Read an avar table from the given font FD. Use the table directory
+ specified in SUBTABLE.
- To use this structure, call
+ Return the avar table upon success, else NULL. */
- sfnt_init_blend (&blend, fvar, gvar)
+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;
- on a `struct sfnt_blend *', with an appropriate fvar and gvar
- table.
+ /* Find the table in the directory. */
- Then, fill in blend.coords with the un-normalized coordinates,
- and call
+ directory = sfnt_find_table (subtable, SFNT_TABLE_AVAR);
- sfnt_normalize_blend (&blend)
+ if (!directory)
+ return NULL;
- finally, call sfnt_vary_glyph and related functions. */
+ min_size = SFNT_ENDOF (struct sfnt_avar_table, axis_count, uint32_t);
-struct sfnt_blend
-{
- /* The fvar table. This determines the number of elements in each
- of the arrays below. */
- struct sfnt_fvar_table *fvar;
+ /* Check that the length is at least min_size. */
+ if (directory->length < min_size)
+ return NULL;
- /* The gvar table. This provides the glyph variation data. */
- struct sfnt_gvar_table *gvar;
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
- /* Un-normalized coordinates. */
- sfnt_fixed *coords;
+ /* Allocate enough to hold the avar table header. */
+ avar = xmalloc (sizeof *avar);
- /* Normalized coordinates. */
- sfnt_fixed *norm_coords;
-};
+ /* Read the avar table header. */
+ rc = read (fd, avar, min_size);
+ if (rc != min_size)
+ goto bail;
-/* Initialize the specified BLEND with the given FVAR and GVAR
- tables. */
+ /* Swap what was read. */
+ sfnt_swap32 (&avar->version);
+ sfnt_swap32 (&avar->axis_count);
-static void
-sfnt_init_blend (struct sfnt_blend *blend, struct sfnt_fvar_table *fvar,
- struct sfnt_gvar_table *gvar)
-{
- size_t size;
+ if (avar->version != 0x00010000)
+ goto bail;
- blend->fvar = fvar;
- blend->gvar = gvar;
+ if (avar->axis_count < 0)
+ goto bail;
- /* 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;
-}
+ /* 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 != size)
+ goto bail1;
-/* Free what was initialized in the specified BLEND. */
+ /* Swap each word. */
+ for (i = 0; i < size / sizeof *buffer; ++i)
+ sfnt_swap16 (&buffer[i]);
-static void
-sfnt_free_blend (struct sfnt_blend *blend)
-{
- xfree (blend->coords);
-}
+ /* Now, determine how big the resulting data needs to be. Each
+ struct has a pointer field, and that should be its alignment. */
-/* Normalize BLEND->fvar->axis_count coordinates in BLEND->coords and
- place the result in BLEND->norm_coords. */
+ 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;
-static void
-sfnt_normalize_blend (struct sfnt_blend *blend)
-{
- struct sfnt_variation_axis *axis;
- int i;
- sfnt_fixed coord;
+ /* Now add one struct sfnt_short_frac_segment for each axis and
+ each of its correspondences. */
+ if (INT_ADD_WRAPV (sizeof (struct sfnt_short_frac_segment),
+ min_size, &min_size)
+ || INT_ADD_WRAPV (sizeof (struct sfnt_short_frac_correspondence)
+ * buffer[k], min_size, &min_size))
+ goto bail1;
- /* 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];
+ /* Verify that words from here to buffer[1 + buffer[k] * 2], the
+ next pairCount field, are within bounds. */
+ if (k + 1 + buffer[k] * 2 > size / sizeof *buffer)
+ goto bail1;
- /* Load the current design coordinate. */
- coord = blend->coords[i];
+ /* Move to the next pairCount field. */
+ k += 1 + buffer[k] * 2;
+ }
- /* Keep it within bounds. */
+ /* 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));
- if (coord > axis->max_value)
- coord = axis->max_value;
- else if (coord < axis->min_value)
- coord = axis->min_value;
+ k = 0;
+ for (i = 0; i < avar->axis_count; ++i)
+ {
+ avar->segments[i].pair_count = buffer[k++];
+ avar->segments[i].correspondence = correspondences;
- 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)
+ for (j = 0; j < avar->segments[i].pair_count; ++j)
{
- 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;
+ correspondences->from_coord = buffer[k++];
+ correspondences->to_coord = buffer[k++];
+ correspondences++;
}
- else
- blend->norm_coords[i] = 0;
}
- /* TODO: process avar tables. */
+ /* Return the read avar table. Free buffer. */
+ xfree (buffer);
+ return avar;
+
+ bail1:
+ xfree (buffer);
+ bail:
+ xfree (avar);
+ return NULL;
}
\f
-struct sfnt_tuple_header
-{
- /* The size in bytes of the serialized data for this tuple variation
- table. */
- uint16_t variation_data_size;
-
- /* A packed field. The high 4 bits are flags (see below). The low 12
- bits are an index into a shared tuple records array. */
- uint16_t tuple_index;
-
- /* Embedded coordinate tuples, if any. */
- sfnt_f2dot14 *embedded_coord;
-
- /* Intermediate coordinate tuples, if any. */
- sfnt_f2dot14 *intermediate_coord;
-
- /* Number of points associated with this tuple.
- Times two, the number of deltas associated with this tuple. */
- uint16_t npoints;
-
- /* Points associated with this tuple. */
- uint16_t *points;
-
- /* Deltas associated with this tuple. */
- sfnt_fword *deltas;
-};
-
-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;
-};
-
/* 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.
sfnt_fword *deltas;
int i, count;
unsigned char control;
+ uint16_t value;
if (data >= end)
return NULL;
if (data + 1 >= end)
goto fail;
- deltas[i] = (signed char) *data++;
- deltas[i] *= 65536;
- deltas[i++] |= *data++;
+ value = *data++ << 8;
+ value |= *data++;
+ deltas[i++] = value;
}
else
{
return NULL;
}
-/* 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. */
+/* 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.
-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)
+ 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)
{
- int i;
- sfnt_fixed coord, start, end;
- sfnt_fixed scale;
+ 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;
- /* scale is initially 1.0. */
- scale = 0200000;
+ /* Find the table in the directory. */
- for (i = 0; i < blend->gvar->axis_count; ++i)
- {
- /* Load values for this axis, scaled up to sfnt_fixed. */
- coord = coords[i] * 4;
+ directory = sfnt_find_table (subtable, SFNT_TABLE_CVAR);
- if (intermediate_p)
- {
- start = intermediate_start[i] * 4;
- end = intermediate_start[i] * 4;
- }
+ if (!directory)
+ return NULL;
- /* Ignore tuples that can be skipped. */
+ min_size = SFNT_ENDOF (struct sfnt_cvar_table, data_offset,
+ uint16_t);
- if (!coord)
- continue;
+ /* Check that the length is at least min_size. */
+ if (directory->length < min_size)
+ return NULL;
- /* If the coordinate is set to 0, then deltas should not be
- applied. Return 0. */
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
- if (!blend->norm_coords[i])
- return 0;
+ /* Allocate enough to hold the cvar table header. */
+ cvar = xmalloc (sizeof *cvar);
- /* If no scaling need take place, continue. */
+ /* Read the cvar table header. */
+ rc = read (fd, cvar, min_size);
+ if (rc != min_size)
+ goto bail;
- if (blend->norm_coords[i] == coord)
- continue;
+ /* 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 != size)
+ goto bail;
- 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. */
+ /* Now figure out how large cvar must be by reading the tuples. */
- if (blend->norm_coords[i] < MIN (0, coord)
- || blend->norm_coords[i] > MAX (0, coord))
- return 0;
+ ntuples = cvar->tuple_count & 0x0fff;
+ data_offset = ((ptrdiff_t) cvar->data_offset
+ - (ptrdiff_t) min_size);
+ end = buffer + size;
- scale = sfnt_multiply_divide_signed (scale,
- blend->norm_coords[i],
- coord);
- }
- else
- {
- /* Otherwise, renormalize between start and end. */
+ if (data_offset < 0)
+ goto bail1;
- if (blend->norm_coords[i] < start
- || blend->norm_coords[i] > end)
- return 0;
+ /* See if there are shared points, and read them if there are. */
- 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);
- }
- }
+ data = buffer + data_offset;
+ tuple = buffer;
+ points = NULL;
- return scale;
-}
+ if (cvar->tuple_count & 0x8000)
+ {
+ points = sfnt_read_packed_points (data, &npoints, end,
+ &tuple);
+ if (!points)
+ goto bail1;
-/* Infer point positions for points that have been partially moved
- within the contour in GLYPH denoted by START and END. */
+ /* Add npoints words to the size. */
+ size = npoints * sizeof *points;
+ }
+ else
+ size = 0;
-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, j;
- sfnt_fword min_pos, max_pos, position;
- sfnt_fixed ratio, delta;
+ while (ntuples--)
+ {
+ data = buffer + data_offset;
- pair_start = pair_first = -1;
+ /* Read the tuple. */
+ if (tuple + 3 >= end)
+ goto bail2;
- /* Look for pairs of touched points. */
+ 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);
- for (i = start; i <= end; ++i)
- {
- if (!touched[i])
- continue;
+ /* Increment the offset to the data by the data size specified
+ here. */
+ data_offset += data_size;
- if (pair_start == -1)
+ if (index & 0x8000)
{
- pair_first = i;
- goto next;
- }
+ /* Embedded coordinates are present. Read each
+ coordinate and add it to the size. */
- pair_end = i;
+ if (tuple + fvar->axis_count * sizeof *coords - 1 >= end)
+ goto bail2;
- /* pair_start to pair_end are now a pair of points, where points
- in between should be interpolated. */
+ tuple += sizeof *coords * fvar->axis_count;
+ if (INT_ADD_WRAPV (size, sizeof *coords * fvar->axis_count,
+ &size))
+ goto bail2;
+ }
+ else
+ /* This table is invalid, as cvar tables don't have global
+ coordinates. */
+ goto bail2;
- for (j = pair_start + 1; j < pair_end; ++j)
+ /* Now read indeterminate tuples if required. */
+ if (index & 0x4000)
{
- /* 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]);
+ tuple += fvar->axis_count * 4;
+ if (INT_ADD_WRAPV (size, fvar->axis_count * 4, &size))
+ goto bail2;
+ }
- /* Now see if the current point lies between min and
- max... */
- if (x[j] >= min_pos && x[j] <= max_pos)
- {
- /* 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. */
+ /* Add one point and one delta for each CVT element. */
+ if (INT_ADD_WRAPV (size, cvt->num_elements * 4, &size))
+ goto bail2;
- if (min_pos == max_pos)
- {
- if ((glyph->simple->x_coordinates[pair_start]
- - x[pair_start])
- == (glyph->simple->x_coordinates[pair_end]
- - x[pair_end]))
- glyph->simple->x_coordinates[j]
- += (glyph->simple->x_coordinates[pair_start]
- - x[pair_start]);
+ /* Now add the size of the tuple. */
+ if (INT_ADD_WRAPV (size, sizeof *cvar->variation, &size))
+ goto bail2;
+ }
- continue;
- }
+ if (INT_ADD_WRAPV (sizeof *cvar, size, &size))
+ goto bail2;
- /* 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));
+ /* 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;
- /* 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]);
+ data_offset = ((ptrdiff_t) cvar->data_offset
+ - (ptrdiff_t) min_size);
- /* 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
+ /* 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)
{
- /* ... otheriwse, move point j by the delta of the
- nearest touched point. */
+ if (tuple + 1 >= end)
+ goto bail2;
- 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;
- }
+ memcpy (coords++, tuple, sizeof *coords);
+ tuple += sizeof *coords;
+ sfnt_swap16 (coords);
+ }
+ }
+ else
+ goto bail2;
- glyph->simple->x_coordinates[j] = x[j] + delta;
+ /* 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);
}
- /* Now, consider the Y axis. */
- min_pos = MIN (y[pair_start], y[pair_end]);
- max_pos = MAX (y[pair_start], y[pair_end]);
+ cvar->variation[i].intermediate_end = coords;
- /* Now see if the current point lies between min and
- max... */
- if (y[j] >= min_pos && y[j] <= max_pos)
+ for (j = 0; j < fvar->axis_count; ++j)
{
- /* 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 (tuple + 1 >= end)
+ goto bail2;
- if (min_pos == max_pos)
- {
- if ((glyph->simple->y_coordinates[pair_start]
- - y[pair_start])
- == (glyph->simple->y_coordinates[pair_end]
- - y[pair_end]))
- glyph->simple->y_coordinates[j]
- += (glyph->simple->y_coordinates[pair_start]
- - y[pair_start]);
+ memcpy (coords++, tuple, sizeof *coords);
+ tuple += sizeof *coords;
+ sfnt_swap16 (coords);
+ }
+ }
- continue;
- }
+ /* Finally, read private ``point'' numbers. If this flag is not
+ set, use shared point numbers previously read.
- /* 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));
+ Read at most CVT->num_elements points, as that is all the
+ storage allocated. */
- /* 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]);
+ if (index & 0x2000)
+ {
+ local = sfnt_read_packed_points (data, &cvar->variation[i].num_points,
+ end, &data);
+ if (!local)
+ goto bail2;
- /* Lerp in between. */
- delta = sfnt_sub (max_pos, min_pos);
- delta = sfnt_mul_fixed (ratio, delta);
- glyph->simple->y_coordinates[j] = min_pos + delta;
+ /* 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
- {
- /* ... otheriwse, move point j by the delta of the
- nearest touched point. */
+ cvar->variation[i].points = NULL;
+ }
+ else
+ {
+ /* Copy in the shared point numbers instead. */
+ cvar->variation[i].num_points = npoints;
- 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;
- }
+ if (npoints != UINT16_MAX)
+ {
+ if (cvar->variation[i].num_points > cvt->num_elements)
+ cvar->variation[i].num_points = cvt->num_elements;
- glyph->simple->y_coordinates[j] = y[j] + delta;
+ 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;
}
- next:
- pair_start = i;
- }
-
- /* If pair_start is set, then lerp points between it and
- pair_first. */
+ /* 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 (pair_start != (size_t) -1)
- {
- j = pair_start + 1;
+ if (cvar->variation[i].num_points == UINT16_MAX)
+ {
+ deltas = sfnt_read_packed_deltas (data, end, cvt->num_elements,
+ &data);
- if (j > end)
- j = start;
+ if (!deltas)
+ goto bail2;
- pair_end = pair_first;
+ cvar->variation[i].deltas = coords;
- while (j != pair_first)
+ for (j = 0; j < cvt->num_elements; ++j)
+ *coords++ = deltas[j];
+ xfree (deltas);
+ }
+ else
{
- /* 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]);
+ deltas = sfnt_read_packed_deltas (data, end,
+ cvar->variation[i].num_points,
+ &data);
+ if (!deltas)
+ goto bail2;
- /* Now see if the current point lies between min and
- max... */
- if (x[j] >= min_pos && x[j] <= max_pos)
- {
- /* 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. */
+ cvar->variation[i].deltas = coords;
- if (min_pos == max_pos)
- {
- if ((glyph->simple->x_coordinates[pair_start]
- - x[pair_start])
- == (glyph->simple->x_coordinates[pair_end]
- - x[pair_end]))
- glyph->simple->x_coordinates[j]
- += (glyph->simple->x_coordinates[pair_start]
- - x[pair_start]);
+ for (j = 0; j < cvar->variation[i].num_points; ++j)
+ *coords++ = deltas[j];
+ xfree (deltas);
+ }
+ }
- goto next_1;
- }
+ /* Free data and return the read cvar table. */
+ if (points != (void *) -1)
+ xfree (points);
+ xfree (buffer);
+ return cvar;
- /* 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));
+ bail2:
+ if (points != (void *) -1)
+ xfree (points);
+ bail1:
+ xfree (buffer);
+ bail:
+ xfree (cvar);
+ return NULL;
+}
- /* 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]);
+\f
- /* 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
- {
- /* ... otheriwse, move point j by the delta of the
- nearest touched point. */
+/* 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. */
- if (x[j] >= max_pos)
- {
- position = MAX (glyph->simple->x_coordinates[pair_start],
- glyph->simple->x_coordinates[pair_end]);
- delta = position - max_pos;
- }
- else
+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->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)
{
- position = MIN (glyph->simple->x_coordinates[pair_start],
- glyph->simple->x_coordinates[pair_end]);
- delta = position - min_pos;
+ 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;
+ }
+ }
+ }
+ }
+}
+
+\f
+
+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, end;
+ 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;
+
+ if (intermediate_p)
+ {
+ start = intermediate_start[i] * 4;
+ end = intermediate_start[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;
+}
+
+/* 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, j;
+ sfnt_fword min_pos, max_pos, position;
+ sfnt_fixed ratio, delta;
+
+ 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, where points
+ in between should be interpolated. */
+
+ for (j = pair_start + 1; j < pair_end; ++j)
+ {
+ /* 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... */
+ if (x[j] >= min_pos && x[j] <= max_pos)
+ {
+ /* 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)
+ {
+ if ((glyph->simple->x_coordinates[pair_start]
+ - x[pair_start])
+ == (glyph->simple->x_coordinates[pair_end]
+ - x[pair_end]))
+ glyph->simple->x_coordinates[j]
+ += (glyph->simple->x_coordinates[pair_start]
+ - x[pair_start]);
+
+ continue;
}
- glyph->simple->x_coordinates[j] = x[j] + delta;
- }
+ /* 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
+ {
+ /* ... otheriwse, move point j by the delta of the
+ nearest touched point. */
+
+ 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;
+ }
+
+ /* 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... */
+ if (y[j] >= min_pos && y[j] <= max_pos)
+ {
+ /* 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)
+ {
+ if ((glyph->simple->y_coordinates[pair_start]
+ - y[pair_start])
+ == (glyph->simple->y_coordinates[pair_end]
+ - y[pair_end]))
+ glyph->simple->y_coordinates[j]
+ += (glyph->simple->y_coordinates[pair_start]
+ - y[pair_start]);
+
+ continue;
+ }
+
+ /* 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
+ {
+ /* ... otheriwse, move point j by the delta of the
+ nearest touched point. */
+
+ 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:
+ pair_start = i;
+ }
+
+ /* If pair_start is set, then lerp points between it and
+ pair_first. */
+
+ if (pair_start != (size_t) -1)
+ {
+ j = pair_start + 1;
+
+ if (j > end)
+ j = start;
+
+ pair_end = pair_first;
+
+ while (j != pair_first)
+ {
+ /* 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... */
+ if (x[j] >= min_pos && x[j] <= max_pos)
+ {
+ /* 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)
+ {
+ if ((glyph->simple->x_coordinates[pair_start]
+ - x[pair_start])
+ == (glyph->simple->x_coordinates[pair_end]
+ - x[pair_end]))
+ glyph->simple->x_coordinates[j]
+ += (glyph->simple->x_coordinates[pair_start]
+ - x[pair_start]);
+
+ goto next_1;
+ }
+
+ /* 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
+ {
+ /* ... otheriwse, move point j by the delta of the
+ nearest touched point. */
+
+ 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;
+ }
+
+ /* 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... */
+ if (y[j] >= min_pos && y[j] <= max_pos)
+ {
+ /* 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)
+ {
+ if ((glyph->simple->y_coordinates[pair_start]
+ - y[pair_start])
+ == (glyph->simple->y_coordinates[pair_end]
+ - y[pair_end]))
+ glyph->simple->y_coordinates[j]
+ += (glyph->simple->y_coordinates[pair_start]
+ - y[pair_start]);
+
+ goto next_1;
+ }
+
+ /* 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
+ {
+ /* ... otheriwse, move point j by the delta of the
+ nearest touched point. */
+
+ 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_1:
+ j++;
+ if (j > end)
+ j = start;
+ }
+ }
+}
+
+/* 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 between the first
+ point and the last 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 positions of each point.
+ TOUCHED contains whether or not each point has been changed by
+ an explicitly specified delta.
+
+ 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 multipled 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. */
+
+ 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 = coords + 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 & 0x1000,
+ 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 (dx[i], scale);
+ glyph->simple->x_coordinates[i] += fword;
+ fword = sfnt_mul_fixed (dy[i], scale);
+ glyph->simple->y_coordinates[i] += fword;
+ }
+
+ /* Apply the deltas for the two phantom points. */
+ distortion->origin += sfnt_mul_fixed (dx[i++], scale);
+ distortion->advance += sfnt_mul_fixed (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;
- /* Now, consider the Y axis. */
- min_pos = MIN (y[pair_start], y[pair_end]);
- max_pos = MAX (y[pair_start], y[pair_end]);
+ /* Deltas are only applied for each point number read. */
- /* Now see if the current point lies between min and
- max... */
- if (y[j] >= min_pos && y[j] <= max_pos)
+ if (!original_x)
{
- /* 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)
- {
- if ((glyph->simple->y_coordinates[pair_start]
- - y[pair_start])
- == (glyph->simple->y_coordinates[pair_end]
- - y[pair_end]))
- glyph->simple->y_coordinates[j]
- += (glyph->simple->y_coordinates[pair_start]
- - y[pair_start]);
+ 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);
- goto next_1;
- }
+ 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);
- /* 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));
+ original_y = original_x + glyph->simple->number_of_points;
+ 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));
+ }
- /* 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]);
+ memset (touched, 0, (sizeof *touched
+ * glyph->simple->number_of_points));
- /* 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
+ for (i = 0; i < point_count; ++i)
{
- /* ... otheriwse, move point j by the delta of the
- nearest touched point. */
+ /* Apply deltas to phantom points. */
- if (y[j] >= max_pos)
+ if (glyph_points[i] == glyph->simple->number_of_points)
{
- position = MAX (glyph->simple->y_coordinates[pair_start],
- glyph->simple->y_coordinates[pair_end]);
- delta = position - max_pos;
+ distortion->origin += sfnt_mul_fixed (dx[i], scale);
+ continue;
}
- else
+
+ if (glyph_points[i] == glyph->simple->number_of_points + 1)
{
- position = MIN (glyph->simple->y_coordinates[pair_start],
- glyph->simple->y_coordinates[pair_end]);
- delta = position - min_pos;
+ distortion->advance += sfnt_mul_fixed (dx[i], scale);
+ continue;
}
- glyph->simple->y_coordinates[j] = y[j] + delta;
+ /* 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 (dx[i], scale);
+ glyph->simple->x_coordinates[glyph_points[i]] += fword;
+ fword = sfnt_mul_fixed (dy[i], scale);
+ glyph->simple->y_coordinates[glyph_points[i]] += fword;
+ touched[glyph_points[i]] = true;
}
- next_1:
- j++;
- if (j > end)
- j = start;
+ sfnt_infer_deltas (glyph, touched, original_x,
+ original_y);
+ break;
}
- }
-}
-
-/* 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 between the first
- point and the last 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.
+ xfree (dx);
+ xfree (dy);
- - 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.
+ if (local_points != (uint16_t *) -1)
+ xfree (local_points);
+ }
- X and Y contain the original positions positions of each point.
- TOUCHED contains whether or not each point has been changed by
- an explicitly specified delta.
+ /* Return success. */
- Apply the inferred deltas back to GLYPH. */
+ if ((glyph->simple->number_of_points
+ * sizeof *touched) >= 1024 * 16)
+ xfree (touched);
-static void
-sfnt_infer_deltas (struct sfnt_glyph *glyph, bool *touched,
- sfnt_fword *x, sfnt_fword *y)
-{
- size_t i;
- int point, first, end;
+ if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16)
+ xfree (coords);
- point = 0;
- for (i = 0; i < glyph->number_of_contours; ++i)
- {
- first = point;
- end = glyph->simple->end_pts_of_contours[i];
+ if ((sizeof *original_x * 2
+ * glyph->simple->number_of_points) >= 1024 * 16)
+ xfree (original_x);
- /* Return if the glyph is invalid. */
+ if (points != (uint16_t *) -1)
+ xfree (points);
- if (first >= glyph->simple->number_of_points
- || end >= glyph->simple->number_of_points
- || first > end)
- return;
+ /* Set the glyph metrics distortion as well. */
+ glyph->advance_distortion = distortion->advance;
+ glyph->origin_distortion = distortion->origin;
- sfnt_infer_deltas_1 (glyph, first, end, touched, x, y);
- point = end + 1;
- }
-}
+ return 0;
-/* 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.
+ fail3:
+ xfree (dx);
+ xfree (dy);
+ xfree (local_points);
+ fail1:
- Value is 0 upon success, else 1.
+ if ((glyph->simple->number_of_points
+ * sizeof *touched) >= 1024 * 16)
+ xfree (touched);
- 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''.
+ if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16)
+ xfree (coords);
- The deltas, one for each point, are multipled by the normalized
- value of each axis and applied to those points for each tuple that
- is found to be applicable.
+ if ((sizeof *original_x * 2
+ * glyph->simple->number_of_points) >= 1024 * 16)
+ xfree (original_x);
- Each element of the glyph variation data is applicable to an axis
- if its list of coordinates:
+ if (points != (uint16_t *) -1)
+ xfree (points);
- - contains one element for each axis, and its axis has a value
- between 0 and that element.
+ return 1;
+}
- - contains two elements for each axis, and its axis has a value
- between the first element and the second.
+/* 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.
- After the deltas are applied, any points without deltas must be
- interpolated similar to an IUP instruction. In addition, deltas
- may also be applied to phantom points within a glyph. */
+ Value is 0 upon success, 1 otherwise. */
-static int
-sfnt_vary_glyph (struct sfnt_blend *blend, sfnt_glyph id,
- struct sfnt_glyph *glyph)
+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;
sfnt_f2dot14 *restrict coords;
sfnt_f2dot14 *restrict intermediate_start;
sfnt_f2dot14 *restrict intermediate_end;
- sfnt_fword *dx, *dy, fword;
+ 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;
bool *touched;
sfnt_fword *restrict original_x, *restrict original_y;
+ struct sfnt_compound_glyph_component *component;
gvar = blend->gvar;
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);
/* Start reading each tuple. */
tuple = gvar->glyph_variation_data + offset + sizeof header;
- coords = xmalloc (gvar->axis_count * sizeof *coords * 3);
+ 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 = coords + gvar->axis_count;
original_x = NULL;
original_y = NULL;
- for (i = 0; i < ntuples; ++i)
+ while (ntuples--)
{
data = gvar->glyph_variation_data + offset + data_offset;
gvar->axis_count * sizeof *coords);
/* Now read indeterminate tuples if required. */
- if (index & 0x1000)
+ if (index & 0x4000)
{
for (j = 0; j < gvar->axis_count; ++j)
{
switch (point_count)
{
case UINT16_MAX:
- /* Deltas are provided for all points in the glyph.
- No glyph should have more than 65535 points. */
-
- if (glyph->simple->number_of_points > 65535)
- abort ();
+ /* Deltas are provided for all components in the glyph. */
/* Add 4 phantom points to each end. */
dx = sfnt_read_packed_deltas (data, end,
- glyph->simple->number_of_points + 4,
+ glyph->compound->num_components + 4,
&data);
dy = sfnt_read_packed_deltas (data, end,
- glyph->simple->number_of_points + 4,
+ glyph->compound->num_components + 4,
&data);
if (!dx || !dy)
goto fail3;
- /* Apply each delta to the simple glyph. */
+ /* Apply each delta to the compound glyph. */
- for (i = 0; i < glyph->simple->number_of_points; ++i)
+ 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 (dx[i], scale);
- glyph->simple->x_coordinates[i] += fword;
+ component->flags |= 01;
+ 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 (dy[i], scale);
- glyph->simple->y_coordinates[i] += fword;
+ component->flags |= 01;
+ component->argument2.d = word + fword;
}
- /* TODO: apply metrics variations. */
+ /* Apply the deltas for the two phantom points. */
+ distortion->origin += sfnt_mul_fixed (dx[i++], scale);
+ distortion->advance += sfnt_mul_fixed (dx[i], scale);
break;
default:
if (!original_x)
{
- touched = xmalloc (sizeof *touched
- * glyph->simple->number_of_points);
- original_x = xmalloc (sizeof *original_x * 2
- * glyph->simple->number_of_points);
- original_y = original_x + glyph->simple->number_of_points;
+ if ((glyph->compound->num_components
+ * sizeof *touched) >= 1024 * 16)
+ touched = xmalloc (sizeof *touched
+ * glyph->compound->num_components);
+ else
+ touched = alloca (sizeof *touched
+ * glyph->compound->num_components);
+
+ if ((sizeof *original_x * 2
+ * glyph->compound->num_components) >= 1024 * 16)
+ original_x = xmalloc (sizeof *original_x * 2
+ * glyph->compound->num_components);
+ else
+ original_x = alloca (sizeof *original_x * 2
+ * glyph->compound->num_components);
+
+ original_y = original_x + glyph->compound->num_components;
memcpy (original_x, glyph->simple->x_coordinates,
(sizeof *original_x
- * glyph->simple->number_of_points));
+ * glyph->compound->num_components));
memcpy (original_y, glyph->simple->y_coordinates,
(sizeof *original_y
- * glyph->simple->number_of_points));
+ * glyph->compound->num_components));
}
memset (touched, 0, (sizeof *touched
- * glyph->simple->number_of_points));
+ * glyph->compound->num_components));
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 (dx[i], scale);
+ continue;
+ }
+
+ if (glyph_points[i] == glyph->compound->num_components + 1)
+ {
+ distortion->advance += sfnt_mul_fixed (dx[i], scale);
+ continue;
+ }
+
/* Make sure the point doesn't end up out of bounds. */
- if (glyph_points[i] >= glyph->simple->number_of_points)
+ 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 (dx[i], scale);
- glyph->simple->x_coordinates[glyph_points[i]] += fword;
+ component->flags |= 01;
+ 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 (dy[i], scale);
- glyph->simple->y_coordinates[glyph_points[i]] += fword;
- touched[glyph_points[i]] = true;
+ component->flags |= 01;
+ component->argument2.d = word + fword;
}
- sfnt_infer_deltas (glyph, touched, original_x,
- original_y);
-
- /* TODO: apply metrics variations. */
break;
}
/* Return success. */
- xfree (touched);
- xfree (coords);
- xfree (original_x);
+ if ((glyph->compound->num_components
+ * sizeof *touched) >= 1024 * 16)
+ xfree (touched);
+
+ if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16)
+ xfree (coords);
+
+ if ((sizeof *original_x * 2
+ * glyph->compound->num_components) >= 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 (dy);
xfree (local_points);
fail1:
- xfree (touched);
- xfree (coords);
- xfree (original_x);
- if (points != (uint16_t *) -1)
- xfree (points);
+ if ((glyph->compound->num_components
+ * sizeof *touched) >= 1024 * 16)
+ xfree (touched);
+
+ if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16)
+ xfree (coords);
+
+ if ((sizeof *original_x * 2
+ * glyph->compound->num_components) >= 1024 * 16)
+ xfree (original_x);
+
+ 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.
+
+ 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;
- return 1;
-}
+ /* Multiply the delta by the interpreter scale factor and
+ then the tuple scale factor. */
+ delta = sfnt_mul_f26dot6_fixed (variation->deltas[j] * 64,
+ interpreter->scale);
+ delta = sfnt_mul_fixed (delta, scale);
-#endif /* TEST */
+ /* Apply the delta to the control value table. */
+ interpreter->cvt[i] += delta;
+ }
+ }
+}
\f
struct sfnt_glyf_table *glyf;
struct sfnt_loca_table_short *loca_short;
struct sfnt_loca_table_long *loca_long;
+ struct sfnt_blend *blend;
};
/* Global context for test functions. Height of glyph. */
}
static struct sfnt_glyph *
-sfnt_test_get_glyph (sfnt_glyph glyph, void *dcontext,
+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;
- return sfnt_read_glyph (glyph, tables->glyf,
- tables->loca_short,
- tables->loca_long);
+ 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
struct sfnt_cmap_encoding_subtable *subtables;
struct sfnt_cmap_encoding_subtable_data **data;
struct sfnt_cmap_table *table;
- int fd, i;
+ int fd, i, j;
sfnt_char character;
struct sfnt_head_table *head;
struct sfnt_hhea_table *hhea;
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;
sfnt_fixed scale;
char *fancy;
int *advances;
char *axis_name;
struct sfnt_instance *instance;
struct sfnt_blend blend;
+ struct sfnt_metrics_distortion distortion;
if (argc < 2)
return 1;
return 1;
}
-#define FANCY_PPEM 12
-#define EASY_PPEM 12
+#define FANCY_PPEM 15
+#define EASY_PPEM 15
interpreter = NULL;
head = sfnt_read_head_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);
+ 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 (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)
{
- loca_long = NULL;
- loca_short = NULL;
length = strlen (fancy);
scale = sfnt_div_fixed (FANCY_PPEM, head->units_per_em);
interpreter = sfnt_make_interpreter (maxp, cvt, head,
FANCY_PPEM, FANCY_PPEM);
+ if (instance && gvar)
+ sfnt_vary_interpreter (interpreter, &blend);
if (!interpreter)
exit (1);
if (!glyph || !glyph->simple)
exit (3);
+ if (instance && gvar)
+ sfnt_vary_simple_glyph (&blend, code, glyph,
+ &distortion);
+
if (sfnt_lookup_glyph_metrics (code, -1,
&metrics,
hmtx, hhea,
xfree (outline);
rasters[i] = raster;
- advances[i] = sfnt_mul_fixed (metrics.advance, scale);
+ advances[i] = (sfnt_mul_fixed (metrics.advance, scale)
+ + sfnt_mul_fixed (distortion.advance, scale));
}
sfnt_x_raster (rasters, advances, length, hhea, scale);
exit (0);
}
- 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,
- FANCY_PPEM,
- FANCY_PPEM);
- state = interpreter->state;
-
- 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);
- }
- }
-
if (hhea && maxp)
hmtx = sfnt_read_hmtx_table (fd, font, hhea, maxp);
(int) hhea->caret_slope_rise,
(int) hhea->caret_slope_run);
- if (fvar)
+ if (head && maxp && maxp->version >= 0x00010000)
{
- 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);
+ 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);
- if (axis_name)
- fprintf (stderr, "axis no: %d; name: %.*s\n",
- i, record.length, axis_name);
- }
+ interpreter = sfnt_make_interpreter (maxp, cvt, head,
+ FANCY_PPEM,
+ FANCY_PPEM);
+ state = interpreter->state;
- 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));
- }
+ if (instance && gvar)
+ sfnt_vary_interpreter (interpreter, &blend);
- for (i = 0; i < fvar->instance_count; ++i)
+ if (fpgm)
{
- if (name)
- {
- axis_name
- = (char *) sfnt_find_name (name, fvar->instance[i].name_id,
- &record);
+ fprintf (stderr, "interpreting the font program, with"
+ " %zu instructions\n", fpgm->num_instructions);
- if (axis_name)
- fprintf (stderr, "instance no: %d; name: %.*s\n",
- i, record.length, axis_name);
- }
+ trap = sfnt_interpret_font_program (interpreter, fpgm);
+
+ if (trap)
+ fprintf (stderr, "**TRAP**: %s\n", trap);
}
- if (fvar->instance_count > 1)
+ if (prep)
{
- printf ("instance? ");
-
- if (scanf ("%d", &i) == EOF)
- goto free_lab;
+ fprintf (stderr, "interpreting the control value program, with"
+ " %zu instructions\n", prep->num_instructions);
- if (i < 0 || i >= fvar->instance_count)
- goto free_lab;
+ trap = sfnt_interpret_control_value_program (interpreter, prep,
+ &state);
- instance = &fvar->instance[i];
+ if (trap)
+ fprintf (stderr, "**TRAP**: %s\n", trap);
}
}
- if (gvar)
- fprintf (stderr, "gvar table found\n");
-
- if (instance && gvar)
- {
- sfnt_init_blend (&blend, fvar, gvar);
-
- for (i = 0; i < fvar->axis_count; ++i)
- blend.coords[i] = instance->coords[i];
-
- sfnt_normalize_blend (&blend);
- }
-
while (true)
{
printf ("table, character? ");
dcontext.loca_short = loca_short;
dcontext.loca_long = loca_long;
+ if (instance && gvar)
+ dcontext.blend = &blend;
+ else
+ dcontext.blend = NULL;
+
if (glyph->simple && instance && gvar)
{
printf ("applying variations to simple glyph...\n");
- if (sfnt_vary_glyph (&blend, code, glyph))
+ 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");
}
&dcontext))
printf ("decomposition failure\n");
+ if (sfnt_lookup_glyph_metrics (code, EASY_PPEM,
+ &metrics,
+ hmtx, hhea,
+ head, 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, head,
EASY_PPEM,
+ &metrics,
sfnt_test_get_glyph,
sfnt_test_free_glyph,
&dcontext);
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 (prep);
xfree (fvar);
xfree (gvar);
-
- if (instance && gvar)
- sfnt_free_blend (&blend);
+ xfree (avar);
+ xfree (cvar);
return 0;
}