From c0873f2382f575b08b5c9eb6663d7a7acf7ece65 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Sun, 26 Mar 2023 14:09:15 +0800 Subject: [PATCH] Update Android port * src/sfnt.c (sfnt_table_names): Add avar. (sfnt_read_glyph): Clear distortion fields. (sfnt_build_glyph_outline): Calculate the outline origin point. (sfnt_prepare_raster): Apply the origin point to the X axis offset. (sfnt_scale_metrics_to_pixel_size): New function. (sfnt_build_instructed_outline): Use instructed origin phantom point to determine the outline origin. (sfnt_compute_phantom_points): Apply origin and advance distortion. (struct sfnt_variation_axis, struct sfnt_instance) (struct sfnt_fvar_table, sfnt_read_fvar_table) (struct sfnt_gvar_table, sfnt_read_gvar_table) (sfnt_read_avar_table, struct sfnt_blend, sfnt_init_blend) (sfnt_free_blend, sfnt_normalize_blend, struct sfnt_tuple_header) (struct sfnt_gvar_glyph_header, sfnt_read_packed_deltas) (sfnt_compute_tuple_scale, sfnt_read_cvar_table) (sfnt_infer_deltas_1, sfnt_vary_simple_glyph, sfnt_infer_deltas) (sfnt_vary_glyph, sfnt_vary_compound_glyph) (sfnt_vary_interpreter): New functions. Add structs to sfntfont.h. (struct sfnt_test_dcontext, sfnt_test_get_glyph, main): Test distortable font handling. * src/sfnt.h (SFNT_ENABLE_HINTING): (enum sfnt_table): (struct sfnt_glyph): (struct sfnt_glyph_outline): (struct sfnt_raster): (struct sfnt_default_uvs_table): (struct sfnt_unicode_value_range): (struct sfnt_nondefault_uvs_table): (struct sfnt_uvs_mapping): (struct sfnt_mapped_variation_selector_record): (struct sfnt_table_offset_rec): (struct sfnt_uvs_context): (struct sfnt_mapped_table): (struct sfnt_variation_axis): (struct sfnt_instance): (struct sfnt_fvar_table): (struct sfnt_short_frac_correspondence): (struct sfnt_short_frac_segment): (struct sfnt_avar_table): (struct sfnt_tuple_variation): (struct sfnt_cvar_table): (struct sfnt_gvar_table): (struct sfnt_blend): (struct sfnt_metrics_distortion): (PROTOTYPE): Update prototypes. * src/sfntfont.c (sfntfont_get_glyph_outline): (sfntfont_measure_pcm): Adjust calls. --- src/sfnt.c | 2572 +++++++++++++++++++++++++++++++++--------------- src/sfnt.h | 583 ++++++++--- src/sfntfont.c | 36 +- 3 files changed, 2245 insertions(+), 946 deletions(-) diff --git a/src/sfnt.c b/src/sfnt.c index 21b2ea96e1c..8e7a30e3b05 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -153,6 +153,7 @@ static uint32_t sfnt_table_names[] = [SFNT_TABLE_FVAR] = 0x66766172, [SFNT_TABLE_GVAR] = 0x67766172, [SFNT_TABLE_CVAR] = 0x63766172, + [SFNT_TABLE_AVAR] = 0x61766172, }; /* Swap values from TrueType to system byte order. */ @@ -2377,6 +2378,10 @@ sfnt_read_glyph (sfnt_glyph glyph_code, 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) @@ -3758,6 +3763,9 @@ sfnt_curve_to_and_build (struct sfnt_point control, 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. @@ -3767,12 +3775,14 @@ TEST_STATIC struct sfnt_glyph_outline * 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); @@ -3825,6 +3835,12 @@ sfnt_build_glyph_outline (struct sfnt_glyph *glyph, 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; } @@ -3887,7 +3903,11 @@ sfnt_prepare_raster (struct sfnt_raster *raster, + (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; } @@ -4516,6 +4536,23 @@ sfnt_scale_metrics (struct sfnt_glyph_metrics *metrics, = 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); +} + /* Font style parsing. */ @@ -4949,8 +4986,6 @@ sfnt_read_ttc_header (int fd) -#ifdef SFNT_ENABLE_HINTING - /* TrueType hinting support. If you do not read the code in this section in conjunction with @@ -10897,6 +10932,15 @@ sfnt_build_instructed_outline (struct sfnt_instructed_outline *instructed) 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); @@ -10935,6 +10979,10 @@ sfnt_compute_phantom_points (struct sfnt_glyph *glyph, 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); @@ -11881,7 +11929,6 @@ sfnt_interpret_compound_glyph (struct sfnt_glyph *glyph, return NULL; } -#endif /* SFNT_ENABLE_HINTING */ @@ -12428,8 +12475,6 @@ sfnt_read_table (int fd, struct sfnt_offset_subtable *subtable, #endif /* !TEST */ -#ifdef TEST - /* Glyph variations. Instead of defining separate fonts for each @@ -12442,83 +12487,12 @@ sfnt_read_table (int fd, struct sfnt_offset_subtable *subtable, 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; @@ -12745,60 +12719,12 @@ sfnt_read_fvar_table (int fd, struct sfnt_offset_subtable *subtable) -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; @@ -12956,160 +12882,130 @@ sfnt_read_gvar_table (int fd, struct sfnt_offset_subtable *subtable) -/* 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; } -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. @@ -13234,6 +13130,7 @@ sfnt_read_packed_deltas (unsigned char *restrict data, sfnt_fword *deltas; int i, count; unsigned char control; + uint16_t value; if (data >= end) return NULL; @@ -13258,9 +13155,9 @@ sfnt_read_packed_deltas (unsigned char *restrict data, 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 { @@ -13282,489 +13179,1323 @@ sfnt_read_packed_deltas (unsigned char *restrict data, 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]); + - /* 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; + } + } + } + } +} + + + +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; @@ -13775,13 +14506,14 @@ sfnt_vary_glyph (struct sfnt_blend *blend, sfnt_glyph id, 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; @@ -13806,6 +14538,10 @@ sfnt_vary_glyph (struct sfnt_blend *blend, sfnt_glyph id, 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); @@ -13846,7 +14582,11 @@ sfnt_vary_glyph (struct sfnt_blend *blend, sfnt_glyph id, /* 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; @@ -13856,7 +14596,7 @@ sfnt_vary_glyph (struct sfnt_blend *blend, sfnt_glyph id, original_x = NULL; original_y = NULL; - for (i = 0; i < ntuples; ++i) + while (ntuples--) { data = gvar->glyph_variation_data + offset + data_offset; @@ -13899,7 +14639,7 @@ sfnt_vary_glyph (struct sfnt_blend *blend, sfnt_glyph id, 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) { @@ -13966,34 +14706,55 @@ sfnt_vary_glyph (struct sfnt_blend *blend, sfnt_glyph id, 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: @@ -14007,39 +14768,83 @@ sfnt_vary_glyph (struct sfnt_blend *blend, sfnt_glyph id, 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; } @@ -14052,13 +14857,24 @@ sfnt_vary_glyph (struct sfnt_blend *blend, sfnt_glyph id, /* 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: @@ -14066,17 +14882,84 @@ sfnt_vary_glyph (struct sfnt_blend *blend, sfnt_glyph id, 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; + } + } +} @@ -14088,6 +14971,7 @@ struct sfnt_test_dcontext 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. */ @@ -14120,17 +15004,31 @@ sfnt_test_curve_to (struct sfnt_point control, } 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 @@ -17881,7 +18779,7 @@ main (int argc, char **argv) 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; @@ -17911,6 +18809,8 @@ main (int argc, char **argv) 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; @@ -17919,6 +18819,7 @@ main (int argc, char **argv) char *axis_name; struct sfnt_instance *instance; struct sfnt_blend blend; + struct sfnt_metrics_distortion distortion; if (argc < 2) return 1; @@ -18029,8 +18930,8 @@ main (int argc, char **argv) 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); @@ -18044,16 +18945,123 @@ main (int argc, char **argv) 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); @@ -18085,6 +19093,8 @@ main (int argc, char **argv) interpreter = sfnt_make_interpreter (maxp, cvt, head, FANCY_PPEM, FANCY_PPEM); + if (instance && gvar) + sfnt_vary_interpreter (interpreter, &blend); if (!interpreter) exit (1); @@ -18128,6 +19138,10 @@ main (int argc, char **argv) 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, @@ -18159,58 +19173,14 @@ main (int argc, char **argv) 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); @@ -18315,82 +19285,54 @@ main (int argc, char **argv) (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? "); @@ -18428,11 +19370,33 @@ main (int argc, char **argv) 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"); } @@ -18444,10 +19408,20 @@ main (int argc, char **argv) &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); @@ -18618,6 +19592,9 @@ main (int argc, char **argv) for (i = 0; i < table->num_subtables; ++i) xfree (data[i]); + if (instance && gvar) + sfnt_free_blend (&blend); + xfree (table); xfree (data); xfree (subtables); @@ -18637,9 +19614,8 @@ main (int argc, char **argv) xfree (prep); xfree (fvar); xfree (gvar); - - if (instance && gvar) - sfnt_free_blend (&blend); + xfree (avar); + xfree (cvar); return 0; } diff --git a/src/sfnt.h b/src/sfnt.h index 82fa343f51d..84e51ff6766 100644 --- a/src/sfnt.h +++ b/src/sfnt.h @@ -27,10 +27,6 @@ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include -#if defined emacs || defined TEST -#define SFNT_ENABLE_HINTING -#endif - /* Container structure and enumerator definitions. */ @@ -56,6 +52,7 @@ enum sfnt_table SFNT_TABLE_FVAR, SFNT_TABLE_GVAR, SFNT_TABLE_CVAR, + SFNT_TABLE_AVAR, }; #define SFNT_ENDOF(type, field, type1) \ @@ -664,6 +661,12 @@ struct sfnt_glyph /* Coordinate bounds. */ sfnt_fword xmin, ymin, xmax, ymax; + /* Distortion applied to the right side phantom point. */ + sfnt_fword advance_distortion; + + /* Distortion applied to the origin point. */ + sfnt_fword origin_distortion; + /* Either a simple glyph or a compound glyph, depending on which is set. */ struct sfnt_simple_glyph *simple; @@ -718,6 +721,10 @@ struct sfnt_glyph_outline and maximum X and Y positions. */ sfnt_fixed xmin, ymin, xmax, ymax; + /* The origin point of the outline on the X axis. Value defaults to + 0. */ + sfnt_fixed origin; + /* Reference count. Initially zero. */ short refcount; }; @@ -739,7 +746,8 @@ struct sfnt_raster /* Basic dimensions of the raster. */ unsigned short width, height; - /* Integer offset to apply to positions in the raster. */ + /* Integer offset to apply to positions in the raster so that they + start from the origin point of the glyph. */ short offx, offy; /* The raster stride. */ @@ -966,6 +974,357 @@ enum sfnt_ttc_tag +/* Unicode Variation Sequence (UVS) support. */ + +struct sfnt_default_uvs_table +{ + /* Number of ranges that follow. */ + uint32_t num_unicode_value_ranges; + + /* Variable length data. */ + struct sfnt_unicode_value_range *ranges; +}; + +struct sfnt_unicode_value_range +{ + /* First value in this range. */ + unsigned int start_unicode_value; + + /* Number of additional values in this range. */ + unsigned char additional_count; +}; + +struct sfnt_nondefault_uvs_table +{ + /* Number of UVS mappings which follow. */ + uint32_t num_uvs_mappings; + + /* Variable length data. */ + struct sfnt_uvs_mapping *mappings; +}; + +struct sfnt_uvs_mapping +{ + /* Base character value. */ + unsigned int unicode_value; + + /* Glyph ID of the base character value. */ + uint16_t base_character_value; +}; + +struct sfnt_mapped_variation_selector_record +{ + /* The variation selector. */ + unsigned int selector; + + /* Its default UVS table. */ + struct sfnt_default_uvs_table *default_uvs; + + /* Its nondefault UVS table. */ + struct sfnt_nondefault_uvs_table *nondefault_uvs; +}; + +/* Structure describing a single offset to load into a variation + selection context. */ + +struct sfnt_table_offset_rec +{ + /* The offset from the start of the font file. */ + off_t offset; + + /* Whether or not the offset points to a non-default UVS table. */ + bool is_nondefault_table; + + /* Pointer to the UVS table. */ + void *table; +}; + +struct sfnt_uvs_context +{ + /* Number of records and tables. */ + size_t num_records, nmemb; + + /* Array of UVS tables. */ + struct sfnt_table_offset_rec *tables; + + /* Array of variation selector records mapped to + their corresponding tables. */ + struct sfnt_mapped_variation_selector_record *records; +}; + + + +#if defined HAVE_MMAP && !defined TEST + +/* Memory mapping support. */ + +struct sfnt_mapped_table +{ + /* Pointer to table data. */ + void *data; + + /* Pointer to table mapping. */ + void *mapping; + + /* Size of mapped data and size of mapping. */ + size_t length, size; +}; + +#endif /* HAVE_MMAP && !TEST */ + + + +/* Glyph variation support. */ + +/* 2.14 fixed point type used to represent versors of unit + vectors. */ +typedef int16_t sfnt_f2dot14; + +/* Forward declaration used only for the distortable font stuff. */ +struct sfnt_cvt_table; + +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; +}; + +struct sfnt_short_frac_correspondence +{ + /* Value in normalized user space. */ + sfnt_f2dot14 from_coord; + + /* Value in normalized axis space. */ + sfnt_f2dot14 to_coord; +}; + +struct sfnt_short_frac_segment +{ + /* The number of pairs for this axis. */ + uint16_t pair_count; + + /* Variable length data. */ + struct sfnt_short_frac_correspondence *correspondence; +}; + +struct sfnt_avar_table +{ + /* The version of the table. Should be 1.0. */ + sfnt_fixed version; + + /* Number of variation axes defined in this table. + XXX: why is this signed? */ + int32_t axis_count; + + /* Variable length data. */ + struct sfnt_short_frac_segment *segments; +}; + +struct sfnt_tuple_variation +{ + /* Tuple point numbers. */ + uint16_t *points; + + /* Deltas. */ + sfnt_fword *deltas; + + /* Tuple coordinates. One for each axis specified in the [gaf]var + tables. */ + sfnt_f2dot14 *coordinates; + + /* Intermediate start and end coordinates. */ + sfnt_f2dot14 *restrict intermediate_start; + + /* Intermediate start and end coordinates. */ + sfnt_f2dot14 *restrict intermediate_end; + + /* The number of points and deltas present. + + UINT16_MAX and POINTS set to NULL means there are deltas for each + CVT entry. */ + uint16_t num_points; +}; + +struct sfnt_cvar_table +{ + /* The version of this CVT variations table. */ + sfnt_fixed version; + + /* Flags. */ + uint16_t tuple_count; + + /* Offset from the beginning of the table to the tuple data. */ + uint16_t data_offset; + + /* Variable length data. */ + struct sfnt_tuple_variation *variation; +}; + +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; +}; + +/* Structure repesenting a set of axis coordinates and their + normalized equivalents. + + To use this structure, call + + sfnt_init_blend (&blend, fvar, gvar) + + on a `struct sfnt_blend *', with an appropriate fvar and gvar + table. + + Then, fill in blend.coords with the un-normalized coordinates, + and call + + sfnt_normalize_blend (&blend) + + finally, call sfnt_vary_simple_glyph and related functions. */ + +struct sfnt_blend +{ + /* The fvar table. This determines the number of elements in each + of the arrays below. */ + struct sfnt_fvar_table *fvar; + + /* The gvar table. This provides the glyph variation data. */ + struct sfnt_gvar_table *gvar; + + /* The avar table. This provides adjustments to normalized axis + values, and may be NULL. */ + struct sfnt_avar_table *avar; + + /* The cvar table. This provides adjustments to CVT values, and may + be NULL. */ + struct sfnt_cvar_table *cvar; + + /* Un-normalized coordinates. */ + sfnt_fixed *coords; + + /* Normalized coordinates. */ + sfnt_fixed *norm_coords; +}; + +struct sfnt_metrics_distortion +{ + /* Distortion applied to the origin point. */ + sfnt_fword origin; + + /* Distortion applied to the advance point. */ + sfnt_fword advance; +}; + + + #define SFNT_CEIL_FIXED(fixed) \ (!((fixed) & 0177777) ? (fixed) \ : ((fixed) + 0200000) & 037777600000) @@ -1012,7 +1371,9 @@ extern void sfnt_free_glyph (struct sfnt_glyph *); #define PROTOTYPE \ struct sfnt_glyph *, \ struct sfnt_head_table *, \ - int, sfnt_get_glyph_proc, \ + int, \ + struct sfnt_glyph_metrics *, \ + sfnt_get_glyph_proc, \ sfnt_free_glyph_proc, \ void * extern struct sfnt_glyph_outline *sfnt_build_glyph_outline (PROTOTYPE); @@ -1042,6 +1403,8 @@ extern int sfnt_lookup_glyph_metrics (sfnt_glyph, int, extern void sfnt_scale_metrics (struct sfnt_glyph_metrics *, sfnt_fixed); +extern void sfnt_scale_metrics_to_pixel_size (struct sfnt_glyph_metrics *, + int, struct sfnt_head_table *); #define PROTOTYPE int, struct sfnt_offset_subtable * extern struct sfnt_name_table *sfnt_read_name_table (PROTOTYPE); @@ -1061,14 +1424,82 @@ extern char *sfnt_find_metadata (struct sfnt_meta_table *, extern struct sfnt_ttc_header *sfnt_read_ttc_header (int); + + +#define PROTOTYPE struct sfnt_cmap_format_14 *, int + +extern struct sfnt_uvs_context *sfnt_create_uvs_context (PROTOTYPE); + +#undef PROTOTYPE + +extern void sfnt_free_uvs_context (struct sfnt_uvs_context *); + +#define PROTOTYPE struct sfnt_nondefault_uvs_table *, sfnt_char + +extern sfnt_glyph sfnt_variation_glyph_for_char (PROTOTYPE); + +#undef PROTOTYPE + + + +#ifdef HAVE_MMAP + +extern int sfnt_map_table (int, struct sfnt_offset_subtable *, + uint32_t, struct sfnt_mapped_table *); +extern int sfnt_unmap_table (struct sfnt_mapped_table *); + +#endif /* HAVE_MMAP */ + + + +extern void *sfnt_read_table (int, struct sfnt_offset_subtable *, + uint32_t, size_t *); + + + +#define PROTOTYPE int, struct sfnt_offset_subtable * + +extern struct sfnt_fvar_table *sfnt_read_fvar_table (PROTOTYPE); +extern struct sfnt_gvar_table *sfnt_read_gvar_table (PROTOTYPE); +extern struct sfnt_avar_table *sfnt_read_avar_table (PROTOTYPE); + +#undef PROTOTYPE + +#define PROTOTYPE \ + int, \ + struct sfnt_offset_subtable *, \ + struct sfnt_fvar_table *, \ + struct sfnt_cvt_table * + +extern struct sfnt_cvar_table *sfnt_read_cvar_table (PROTOTYPE); + +#undef PROTOTYPE + + + +extern void sfnt_init_blend (struct sfnt_blend *, + struct sfnt_fvar_table *, + struct sfnt_gvar_table *, + struct sfnt_avar_table *, + struct sfnt_cvar_table *); +extern void sfnt_free_blend (struct sfnt_blend *); +extern void sfnt_normalize_blend (struct sfnt_blend *); + + + +extern int sfnt_vary_simple_glyph (struct sfnt_blend *, sfnt_glyph, + struct sfnt_glyph *, + struct sfnt_metrics_distortion *); +extern int sfnt_vary_compound_glyph (struct sfnt_blend *, sfnt_glyph, + struct sfnt_glyph *, + struct sfnt_metrics_distortion *); + #endif /* TEST */ /* TrueType hinting support. */ -#ifdef SFNT_ENABLE_HINTING - /* Structure definitions for tables used by the TrueType interpreter. */ @@ -1107,10 +1538,6 @@ struct sfnt_prep_table /* 26.6 fixed point type used within the interpreter. */ typedef int32_t sfnt_f26dot6; -/* 2.14 fixed point type used to represent versors of unit - vectors. */ -typedef int16_t sfnt_f2dot14; - /* 18.14 fixed point type used to calculate rounding details. */ typedef int32_t sfnt_f18dot14; @@ -1479,106 +1906,6 @@ struct sfnt_instructed_outline -/* Unicode Variation Sequence (UVS) support. */ - -struct sfnt_default_uvs_table -{ - /* Number of ranges that follow. */ - uint32_t num_unicode_value_ranges; - - /* Variable length data. */ - struct sfnt_unicode_value_range *ranges; -}; - -struct sfnt_unicode_value_range -{ - /* First value in this range. */ - unsigned int start_unicode_value; - - /* Number of additional values in this range. */ - unsigned char additional_count; -}; - -struct sfnt_nondefault_uvs_table -{ - /* Number of UVS mappings which follow. */ - uint32_t num_uvs_mappings; - - /* Variable length data. */ - struct sfnt_uvs_mapping *mappings; -}; - -struct sfnt_uvs_mapping -{ - /* Base character value. */ - unsigned int unicode_value; - - /* Glyph ID of the base character value. */ - uint16_t base_character_value; -}; - -struct sfnt_mapped_variation_selector_record -{ - /* The variation selector. */ - unsigned int selector; - - /* Its default UVS table. */ - struct sfnt_default_uvs_table *default_uvs; - - /* Its nondefault UVS table. */ - struct sfnt_nondefault_uvs_table *nondefault_uvs; -}; - -/* Structure describing a single offset to load into a variation - selection context. */ - -struct sfnt_table_offset_rec -{ - /* The offset from the start of the font file. */ - off_t offset; - - /* Whether or not the offset points to a non-default UVS table. */ - bool is_nondefault_table; - - /* Pointer to the UVS table. */ - void *table; -}; - -struct sfnt_uvs_context -{ - /* Number of records and tables. */ - size_t num_records, nmemb; - - /* Array of UVS tables. */ - struct sfnt_table_offset_rec *tables; - - /* Array of variation selector records mapped to - their corresponding tables. */ - struct sfnt_mapped_variation_selector_record *records; -}; - - - -#if defined HAVE_MMAP && !defined TEST - -/* Memory mapping support. */ - -struct sfnt_mapped_table -{ - /* Pointer to table data. */ - void *data; - - /* Pointer to table mapping. */ - void *mapping; - - /* Size of mapped data and size of mapping. */ - size_t length, size; -}; - -#endif /* HAVE_MMAP && !TEST */ - - - /* Functions used to read tables used by the TrueType interpreter. */ #ifndef TEST @@ -1653,39 +1980,11 @@ extern const char *sfnt_interpret_compound_glyph (PROTOTYPE); -#define PROTOTYPE struct sfnt_cmap_format_14 *, int - -extern struct sfnt_uvs_context *sfnt_create_uvs_context (PROTOTYPE); - -#undef PROTOTYPE - -extern void sfnt_free_uvs_context (struct sfnt_uvs_context *); - -#define PROTOTYPE struct sfnt_nondefault_uvs_table *, sfnt_char - -extern sfnt_glyph sfnt_variation_glyph_for_char (PROTOTYPE); - -#undef PROTOTYPE - - - -#ifdef HAVE_MMAP - -extern int sfnt_map_table (int, struct sfnt_offset_subtable *, - uint32_t, struct sfnt_mapped_table *); -extern int sfnt_unmap_table (struct sfnt_mapped_table *); - -#endif /* HAVE_MMAP */ - - - -extern void *sfnt_read_table (int, struct sfnt_offset_subtable *, - uint32_t, size_t *); +extern void sfnt_vary_interpreter (struct sfnt_interpreter *, + struct sfnt_blend *); #endif /* TEST */ -#endif /* SFNT_ENABLE_HINTING */ - #endif /* _SFNT_H_ */ diff --git a/src/sfntfont.c b/src/sfntfont.c index 500256d6fb4..e4579d62154 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -1688,6 +1688,8 @@ sfntfont_dereference_outline (struct sfnt_glyph_outline *outline) If INTERPRETER is non-NULL, then possibly use the unscaled glyph metrics in METRICS and the interpreter STATE to instruct the glyph. + Otherwise, METRICS must contain scaled glyph metrics used to + compute the origin point of the outline. Return the outline with an incremented reference count and enter the generated outline into CACHE upon success, possibly discarding @@ -1714,6 +1716,7 @@ sfntfont_get_glyph_outline (sfnt_glyph glyph_code, struct sfntfont_get_glyph_outline_dcontext dcontext; struct sfnt_instructed_outline *value; const char *error; + struct sfnt_glyph_metrics temp; start = cache->next; @@ -1784,10 +1787,31 @@ sfntfont_get_glyph_outline (sfnt_glyph glyph_code, } if (!outline) - outline = sfnt_build_glyph_outline (glyph, head, pixel_size, - sfntfont_get_glyph, - sfntfont_free_glyph, - &dcontext); + { + /* If INTERPRETER is NULL, METRICS contains scaled metrics. */ + + if (!interpreter) + outline = sfnt_build_glyph_outline (glyph, head, pixel_size, + metrics, + sfntfont_get_glyph, + sfntfont_free_glyph, + &dcontext); + else + { + /* But otherwise, they are unscaled, and must be scaled + before being used. */ + + temp = *metrics; + sfnt_scale_metrics_to_pixel_size (&temp, pixel_size, + head); + outline = sfnt_build_glyph_outline (glyph, head, pixel_size, + &temp, + sfntfont_get_glyph, + sfntfont_free_glyph, + &dcontext); + } + } + xfree (glyph); if (!outline) @@ -2689,8 +2713,8 @@ sfntfont_measure_pcm (struct sfnt_font_info *font, sfnt_glyph glyph, font->hmtx, font->hhea, font->maxp, font->loca_short, - font->loca_long, NULL, NULL, - NULL); + font->loca_long, NULL, + &metrics, NULL); if (!outline) return 1; -- 2.39.2