From b78ef9bcd1bf34bec877b4fd30f2bbb97cb0919c Mon Sep 17 00:00:00 2001 From: Po Lu Date: Fri, 24 Mar 2023 10:43:34 +0800 Subject: [PATCH] Update Android port * src/sfnt.c (sfnt_table_names): Add fvar, gvar, cvar. (sfnt_read_maxp_table): Call xmalloc, not malloc. (sfnt_read_simple_glyph): Avoid use-after-free if simple is invalid. (sfnt_fill_span): Fix max coverage. (sfnt_normalize_vector): Fail if magnitude is zero. (sfnt_measure_distance): Fix opcode order. (sfnt_dot_fix_14): Fix implementation. (struct sfnt_variation_axis, struct sfnt_instance) (struct sfnt_fvar_table, struct sfnt_gvar_table) (sfnt_read_fvar_table, sfnt_read_gvar_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_points, sfnt_read_packed_deltas) (sfnt_compute_tuple_scale, sfnt_infer_deltas_1, sfnt_infer_deltas) (sfnt_vary_glyph): Add WIP variation glyph implementation. * src/sfnt.h (enum sfnt_table, struct sfnt_simple_glyph): Likewise. --- src/sfnt.c | 2253 ++++++++++++++++++++++++++++++++++++++++++++++------ src/sfnt.h | 24 +- 2 files changed, 2017 insertions(+), 260 deletions(-) diff --git a/src/sfnt.c b/src/sfnt.c index bdd713aa016..21b2ea96e1c 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -150,6 +150,9 @@ static uint32_t sfnt_table_names[] = [SFNT_TABLE_CVT ] = 0x63767420, [SFNT_TABLE_FPGM] = 0x6670676d, [SFNT_TABLE_PREP] = 0x70726570, + [SFNT_TABLE_FVAR] = 0x66766172, + [SFNT_TABLE_GVAR] = 0x67766172, + [SFNT_TABLE_CVAR] = 0x63766172, }; /* Swap values from TrueType to system byte order. */ @@ -1614,7 +1617,7 @@ sfnt_read_maxp_table (int fd, struct sfnt_offset_subtable *subtable) directory->length or sizeof *maxp bytes into it, whichever is smaller. */ - maxp = malloc (sizeof *maxp); + maxp = xmalloc (sizeof *maxp); size = MIN (directory->length, sizeof *maxp); rc = read (fd, maxp, size); @@ -1917,6 +1920,7 @@ sfnt_read_simple_glyph (struct sfnt_glyph *glyph, { glyph->simple = NULL; xfree (simple); + return; } /* Repeat the current flag until @@ -4227,8 +4231,10 @@ sfnt_fill_span (struct sfnt_raster *raster, sfnt_fixed y, } /* Clear coverage info for first pixel. Compute coverage for center - pixels. */ - w = coverage[SFNT_POLY_MASK]; + pixels. Note that SFNT_POLY_SAMPLE is used and not + SFNT_POLY_MASK, because coverage has a blank column at the + start. */ + w = coverage[SFNT_POLY_SAMPLE]; /* Fill pixels between left and right. */ while (left + SFNT_POLY_MASK < right) @@ -8080,6 +8086,7 @@ sfnt_normalize_vector (sfnt_f26dot6 vx, sfnt_f26dot6 vy, /* If vx and vy are both zero, then just project horizontally. */ + fail: vector->x = 04000; vector->y = 0; return; @@ -8113,6 +8120,10 @@ sfnt_normalize_vector (sfnt_f26dot6 vx, sfnt_f26dot6 vy, /* Get hypotenuse of the triangle from vx, 0, to 0, vy. */ magnitude = sfnt_sqrt_fixed (n); + /* Avoid division by zero. */ + if (!magnitude) + goto fail; + /* Long division.. eek! */ vector->x = (sfnt_div_fixed (vx * 1024, magnitude) >> 2); vector->y = (sfnt_div_fixed (vy * 1024, magnitude) >> 2); @@ -8169,8 +8180,8 @@ sfnt_line_to_vector (struct sfnt_interpreter *interpreter, relative to the projection or dual projection vector. Return the distance of P1 and P2 relative to their original - un-instructed positions should OPCODE be 0x49, and to their - instructed positions should OPCODE be 0x4A. */ + un-instructed positions should OPCODE be 0x4A, and to their + instructed positions should OPCODE be 0x49. */ static sfnt_f26dot6 sfnt_measure_distance (struct sfnt_interpreter *interpreter, @@ -8188,25 +8199,9 @@ sfnt_measure_distance (struct sfnt_interpreter *interpreter, sfnt_address_zp1 (interpreter, p2, &p2x, &p2y, &p2_original_x, &p2_original_y); - if (opcode == 0x49) - { - /* When measuring in the glyph zone, measure the distance using - the dual projection vector, relative to the ``original - original outlines''. - - This is not written down anywhere, leading you to believe - that the distance is measured using the scaled outline prior - to instructing. */ - - if (interpreter->state.zp0 == 1 - && interpreter->state.zp1 == 1) - return sfnt_div_fixed (DUAL_PROJECT (sfnt_sub (p1x, p2x), - sfnt_sub (p1y, p2y)), - interpreter->scale); - - return DUAL_PROJECT (sfnt_sub (p1x, p2x), - sfnt_sub (p1y, p2y)); - } + if (opcode == 0x4A) + return DUAL_PROJECT (sfnt_sub (p1_original_x, p2_original_x), + sfnt_sub (p1_original_y, p2_original_y)); return PROJECT (sfnt_sub (p1x, p2x), sfnt_sub (p1y, p2y)); @@ -9238,7 +9233,7 @@ sfnt_dot_fix_14 (int32_t ax, int32_t ay, int bx, int by) yy = xx >> 63; xx += 0x2000 + yy; - return (int32_t) (yy / (2 << 14)); + return (int32_t) (xx / (2 << 14)); #endif } @@ -11454,6 +11449,8 @@ sfnt_interpret_compound_glyph_1 (struct sfnt_glyph *glyph, struct sfnt_instructed_outline *value; struct sfnt_glyph_metrics sub_metrics; + error = NULL; + /* Set up the base index. This is the index from where on point renumbering starts. @@ -12431,278 +12428,1928 @@ sfnt_read_table (int fd, struct sfnt_offset_subtable *subtable, #endif /* !TEST */ +#ifdef TEST + -#ifdef TEST +/* Glyph variations. Instead of defining separate fonts for each + combination of weight, width and slant (bold, condensed, italic, + etc), some fonts specify a list of ``variation axes'', each of + which determines one delta to apply to each point in every + glyph. -struct sfnt_test_dcontext + This optional information is specified in the `fvar' (font + variation), `gvar' (glyph variation) and `cvar' (CVT variation) + tables in a font file. */ + +struct sfnt_variation_axis { - /* Context for sfnt_test_get_glyph. */ - struct sfnt_glyf_table *glyf; - struct sfnt_loca_table_short *loca_short; - struct sfnt_loca_table_long *loca_long; -}; + /* The axis tag. */ + uint32_t axis_tag; -/* Global context for test functions. Height of glyph. */ -static sfnt_fixed sfnt_test_max; + /* The minimum style coordinate for the axis. */ + sfnt_fixed min_value; -static void -sfnt_test_move_to (struct sfnt_point point, void *dcontext) -{ - printf ("move_to: %g, %g\n", sfnt_coerce_fixed (point.x), - sfnt_coerce_fixed (point.y)); -} + /* The default style coordinate for the axis. */ + sfnt_fixed default_value; -static void -sfnt_test_line_to (struct sfnt_point point, void *dcontext) -{ - printf ("line_to: %g, %g\n", sfnt_coerce_fixed (point.x), - sfnt_coerce_fixed (point.y)); -} + /* The maximum style coordinate for the axis. */ + sfnt_fixed max_value; -static void -sfnt_test_curve_to (struct sfnt_point control, - struct sfnt_point endpoint, - void *dcontext) -{ - printf ("curve_to: %g, %g - %g, %g\n", - sfnt_coerce_fixed (control.x), - sfnt_coerce_fixed (control.y), - sfnt_coerce_fixed (endpoint.x), - sfnt_coerce_fixed (endpoint.y)); -} + /* Set to zero. */ + uint16_t flags; -static struct sfnt_glyph * -sfnt_test_get_glyph (sfnt_glyph glyph, void *dcontext, - bool *need_free) + /* Identifier under which this axis's name will be found in the + `name' table. */ + uint16_t name_id; +}; + +struct sfnt_instance { - struct sfnt_test_dcontext *tables; + /* The instance name ID. */ + uint16_t name_id; - tables = dcontext; - *need_free = true; + /* Flags. */ + uint16_t flags; - return sfnt_read_glyph (glyph, tables->glyf, - tables->loca_short, - tables->loca_long); -} + /* Optional PostScript name. */ + uint16_t ps_name_id; -static void -sfnt_test_free_glyph (struct sfnt_glyph *glyph, void *dcontext) -{ - sfnt_free_glyph (glyph); -} + /* Coordinates of each defined instance. */ + sfnt_fixed *coords; +}; -static void -sfnt_test_span (struct sfnt_edge *edge, sfnt_fixed y, - void *dcontext) +struct sfnt_fvar_table { -#if 1 - printf ("/* span at %g */\n", sfnt_coerce_fixed (y)); - for (; edge; edge = edge->next) - { - if (y >= edge->bottom && y < edge->top) - printf ("ctx.fillRect (%g, %g, 1, 1); " - "/* %g top: %g bot: %g stepx: %g winding: %d */\n", - sfnt_coerce_fixed (edge->x), - sfnt_coerce_fixed (sfnt_test_max - y), - sfnt_coerce_fixed (y), - sfnt_coerce_fixed (edge->top), - sfnt_coerce_fixed (edge->bottom), - sfnt_coerce_fixed (edge->step_x), - edge->winding); - else - printf ("STRIPPED BAD SPAN!!! %g %g %"PRIi32 - " %"PRIi32" (winding: %d)\n", - sfnt_coerce_fixed (edge->top), - sfnt_coerce_fixed (edge->bottom), - edge->top, y, edge->winding); - } -#elif 0 - int winding; - short x, dx; - - winding = 0; - x = 0; + /* Major version; should be 1. */ + uint16_t major_version; - for (; edge; edge = edge->next) - { - dx = (edge->x >> 16) - x; - x = edge->x >> 16; + /* Minor version; should be 0. */ + uint16_t minor_version; - for (; dx > 0; --dx) - putc (winding ? '.' : ' ', stdout); + /* Offset in bytes from the beginning of the table to the beginning + of the first axis data. */ + uint16_t offset_to_data; - winding = !winding; - } + /* Reserved field; always 2. */ + uint16_t count_size_pairs; - putc ('\n', stdout); -#elif 0 - for (; edge; edge = edge->next) - printf ("%g-", sfnt_coerce_fixed (edge->x)); - puts (""); -#endif -} + /* Number of style axes in this font. */ + uint16_t axis_count; -static void -sfnt_test_edge_ignore (struct sfnt_edge *edges, size_t num_edges, - void *dcontext) -{ + /* 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 same debugger stuff is used here. */ -static void sfnt_setup_debugger (void); + /* The size of each instance record. */ + uint16_t instance_size; -/* The debugger's X display. */ -static Display *display; + /* Variable length data. */ + struct sfnt_variation_axis *axis; + struct sfnt_instance *instance; +}; -/* The debugger window. */ -static Window window; +/* Read an fvar table from the given font FD. Use the table directory + specified in SUBTABLE. -/* The GC. */ -static GC point_gc, background_gc; + Return the fvar table upon success, else NULL. */ -static void -sfnt_test_edges (struct sfnt_edge *edges, size_t num_edges) +static struct sfnt_fvar_table * +sfnt_read_fvar_table (int fd, struct sfnt_offset_subtable *subtable) { - static sfnt_fixed y; - size_t i; + struct sfnt_table_directory *directory; + struct sfnt_fvar_table *fvar; + ssize_t rc; + size_t min_bytes, ps_size, non_ps_size, temp, pad; + off_t offset; + int i, j; + char *buffer; + sfnt_fixed *coords; - for (i = 0; i < num_edges; ++i) - { - if (y >= edges[i].bottom && y < edges[i].top) - { - XDrawPoint (display, window, point_gc, - edges[i].x / 65536, 100 - (y / 65536)); - printf ("sfnt_test_edges: %d %d\n", - edges[i].x / 65536, 100 - (y / 65536)); - } - } + /* Find the table in the directory. */ - y += SFNT_POLY_STEP; + directory = sfnt_find_table (subtable, SFNT_TABLE_FVAR); - for (i = 0; i < num_edges; ++i) - sfnt_step_edge (&edges[i]); -} + if (!directory) + return NULL; -static void -sfnt_debug_edges (struct sfnt_edge *edges, size_t num_edges) -{ - XEvent event; + min_bytes = SFNT_ENDOF (struct sfnt_fvar_table, + instance_size, uint16_t); - sfnt_setup_debugger (); + /* Check that the length is at least min_bytes. */ + if (directory->length < min_bytes) + return NULL; - while (true) - { - XNextEvent (display, &event); + /* Seek to the location given in the directory. */ + if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1) + return NULL; - switch (event.type) - { - case KeyPress: - XDestroyWindow (display, window); - XCloseDisplay (display); - exit (0); - break; + /* Allocate enough to hold the fvar table header. */ + fvar = xmalloc (sizeof *fvar); - case Expose: + /* Read the fvar table header. */ + buffer = NULL; + rc = read (fd, fvar, min_bytes); + if (rc != min_bytes) + goto bail; - while (true) - { - sfnt_test_edges (edges, num_edges); - XFlush (display); - usleep (50000); - } + /* Swap what was read. */ + sfnt_swap16 (&fvar->major_version); + sfnt_swap16 (&fvar->minor_version); + sfnt_swap16 (&fvar->offset_to_data); + sfnt_swap16 (&fvar->count_size_pairs); + sfnt_swap16 (&fvar->axis_count); + sfnt_swap16 (&fvar->axis_size); + sfnt_swap16 (&fvar->instance_count); + sfnt_swap16 (&fvar->instance_size); - break; - } - } -} + /* major_version should be 1, and minor_version 0. */ -static void -sfnt_test_edge (struct sfnt_edge *edges, size_t num_edges, - void *dcontext) -{ - size_t i; + if (fvar->major_version != 1 || fvar->minor_version) + goto bail; - printf ("built %zu edges\n", num_edges); + /* count_size_pairs should be more than 2. */ - for (i = 0; i < num_edges; ++i) - { - printf ("/* edge x, top, bot: %g, %g - %g. winding: %d */\n" - "/* edge step_x: %g */\n", - sfnt_coerce_fixed (edges[i].x), - sfnt_coerce_fixed (edges[i].top), - sfnt_coerce_fixed (edges[i].bottom), - edges[i].winding, - sfnt_coerce_fixed (edges[i].step_x)); -#ifdef TEST_VERTEX - printf ("ctx.fillRect (%g, %g, 1, 1);\n", - sfnt_coerce_fixed (edges[i].x), - sfnt_coerce_fixed (sfnt_test_max - - edges[i].y)); -#else - printf ("ctx.fillRect (%g, %g, 1, 1);\n", - sfnt_coerce_fixed (edges[i].x), - sfnt_coerce_fixed (sfnt_test_max - - edges[i].bottom)); -#endif - } + if (fvar->count_size_pairs < 2) + goto bail; - if (getenv ("SFNT_DEBUG_STEP")) - { - if (!fork ()) - sfnt_debug_edges (edges, num_edges); - } + /* Don't try to read tables where the axis format differs. */ - printf ("==end of edges==\n"); + if (fvar->axis_size != 20) + goto bail; - sfnt_poly_edges (edges, num_edges, sfnt_test_span, NULL); -} + /* The instance size must either be 2 * sizeof (uint16_t) + + axisCount * sizeof (sfnt_fixed), meaning there is no PostScript + name identifier, or 3 * sizeof (uint16_t) + axisCount * sizeof + (sfnt_fixed), meaning there is. */ -static void -sfnt_x_raster (struct sfnt_raster **rasters, - int *advances, - int nrasters, - struct sfnt_hhea_table *hhea, - sfnt_fixed scale) -{ - Display *display; - Window window; - Pixmap *pixmaps; - Picture *glyphs, drawable, solid; - int event_base, error_base; - int major, minor, *depths, count; - XRenderPictFormat *format, *glyph_format; - Visual *visual; - XImage image; - GC gc; - XGCValues gcvalues; - XEvent event; - XRenderColor white, black; - int i, ascent, origin, x, y; - Font font; + if (INT_MULTIPLY_WRAPV (fvar->axis_count, sizeof (sfnt_fixed), + &temp) + || INT_ADD_WRAPV (2 * sizeof (uint16_t), temp, &non_ps_size)) + goto bail; - if (!nrasters) - exit (0); + if (INT_MULTIPLY_WRAPV (fvar->axis_count, sizeof (sfnt_fixed), + &temp) + || INT_ADD_WRAPV (3 * sizeof (uint16_t), temp, &ps_size)) + goto bail; - display = XOpenDisplay (NULL); + if (fvar->instance_size != non_ps_size + && fvar->instance_size != ps_size) + goto bail; - if (!display) - exit (0); + /* Now compute the offset of the axis data from the start of the + font file. */ - if (!XRenderQueryExtension (display, &event_base, &error_base) - || !XRenderQueryVersion (display, &major, &minor)) - exit (0); + if (INT_ADD_WRAPV (fvar->offset_to_data, directory->offset, + &offset)) + goto bail; - if (major == 0 && minor < 10) - exit (0); + /* Seek there. */ - window = XCreateSimpleWindow (display, DefaultRootWindow (display), - 0, 0, 100, 150, 0, 0, - WhitePixel (display, - DefaultScreen (display))); - XSelectInput (display, window, ExposureMask); - XMapWindow (display, window); + if (lseek (fd, offset, SEEK_SET) != offset) + goto bail; + + min_bytes = sizeof *fvar; + + /* Now, read each axis and instance. Calculate how much extra data + needs to be allocated for the axes and instances: this is + fvar->axis_count * sizeof (struct sfnt_variation_axis), some + padding, and finally fvar->instance_count * sizeof (struct + sfnt_instance) + sizeof (sfnt_fixed) * fvar->instance_count * + fvar->axis_count. */ + + if (INT_MULTIPLY_WRAPV (fvar->axis_count, sizeof *fvar->axis, + &temp) + || INT_ADD_WRAPV (min_bytes, temp, &min_bytes)) + goto bail; + + pad = alignof (struct sfnt_variation_axis); + pad -= min_bytes & (pad - 1); + + if (INT_ADD_WRAPV (min_bytes, pad, &min_bytes)) + goto bail; + + if (INT_MULTIPLY_WRAPV (fvar->instance_count, + sizeof *fvar->instance, + &temp) + || INT_ADD_WRAPV (min_bytes, temp, &min_bytes)) + goto bail; + + if (INT_MULTIPLY_WRAPV (fvar->instance_count, + sizeof *fvar->instance->coords, + &temp) + || INT_MULTIPLY_WRAPV (temp, fvar->axis_count, &temp) + || INT_ADD_WRAPV (min_bytes, temp, &min_bytes)) + goto bail; + + /* Reallocate fvar. */ + fvar = xrealloc (fvar, min_bytes); + + /* Fill in offsets. */ + fvar->axis = (struct sfnt_variation_axis *) (fvar + 1); + fvar->instance + = (struct sfnt_instance *) (((char *) (fvar->axis + + fvar->axis_count)) + + pad); + + /* Read axes. */ + + if (directory->length - SFNT_ENDOF (struct sfnt_fvar_table, + instance_size, uint16_t) + < sizeof *fvar->axis * fvar->axis_count) + goto bail; + + rc = read (fd, fvar->axis, sizeof *fvar->axis * fvar->axis_count); + if (rc != sizeof *fvar->axis * fvar->axis_count) + goto bail; + + /* Swap each axis. */ + + for (i = 0; i < fvar->axis_count; ++i) + { + sfnt_swap32 (&fvar->axis[i].axis_tag); + sfnt_swap32 (&fvar->axis[i].min_value); + sfnt_swap32 (&fvar->axis[i].default_value); + sfnt_swap32 (&fvar->axis[i].max_value); + sfnt_swap16 (&fvar->axis[i].flags); + sfnt_swap16 (&fvar->axis[i].name_id); + } + + /* Read each instance. */ + + if (fvar->instance_size < 1024 * 16) + buffer = alloca (fvar->instance_size); + else + buffer = xmalloc (fvar->instance_size); + + coords = (sfnt_fixed *) (fvar->instance + fvar->instance_count); + + for (i = 0; i < fvar->instance_count; ++i) + { + rc = read (fd, buffer, fvar->instance_size); + if (rc != fvar->instance_size) + goto bail; + + /* Fill in various fields. */ + + fvar->instance[i].name_id = *((uint16_t *) buffer); + fvar->instance[i].flags = *((uint16_t *) buffer + 1); + fvar->instance[i].ps_name_id = 0; + + sfnt_swap16 (&fvar->instance[i].name_id); + sfnt_swap16 (&fvar->instance[i].flags); + + /* Read coordinates. */ + + fvar->instance[i].coords = coords; + coords += fvar->axis_count; + + memcpy (fvar->instance[i].coords, buffer + 4, + sizeof *fvar->instance[i].coords * fvar->axis_count); + + /* Swap coordinates. */ + + for (j = 0; j < fvar->axis_count; ++j) + sfnt_swap32 (&fvar->instance[i].coords[j]); + + /* Read the PostScript name ID if necessary. If not, set it to + nil. */ + + if (fvar->instance_size == ps_size) + { + fvar->instance[i].ps_name_id + = *(uint16_t *) (buffer + 4 + (sizeof *fvar->instance[i].coords + * fvar->axis_count)); + sfnt_swap16 (&fvar->instance[i].ps_name_id); + } + } + + /* Free the temporary buffer. */ + if (buffer && fvar->instance_size >= 1024 * 16) + xfree (buffer); + + /* Return the fvar table. */ + return fvar; + + bail: + if (buffer && fvar->instance_size >= 1024 * 16) + xfree (buffer); + + xfree (fvar); + return NULL; +} + + + +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 * +sfnt_read_gvar_table (int fd, struct sfnt_offset_subtable *subtable) +{ + struct sfnt_table_directory *directory; + struct sfnt_gvar_table *gvar; + ssize_t rc; + size_t min_bytes, off_size, coordinate_size, data_size; + int i; + off_t offset; + + /* Find the table in the directory. */ + + directory = sfnt_find_table (subtable, SFNT_TABLE_GVAR); + + if (!directory) + return NULL; + + min_bytes = SFNT_ENDOF (struct sfnt_gvar_table, + offset_to_data, uint32_t); + + /* Check that the length is at least min_bytes. */ + if (directory->length < min_bytes) + return NULL; + + /* Seek to the location given in the directory. */ + if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1) + return NULL; + + /* Allocate enough to hold the gvar table header. */ + gvar = xmalloc (sizeof *gvar); + + /* Read the gvar table header. */ + rc = read (fd, gvar, min_bytes); + if (rc != min_bytes) + goto bail; + + /* Swap what was read. */ + sfnt_swap16 (&gvar->version); + sfnt_swap16 (&gvar->reserved); + sfnt_swap16 (&gvar->axis_count); + sfnt_swap16 (&gvar->shared_coord_count); + sfnt_swap32 (&gvar->offset_to_coord); + sfnt_swap16 (&gvar->glyph_count); + sfnt_swap16 (&gvar->flags); + sfnt_swap32 (&gvar->offset_to_data); + + if (gvar->version != 1) + goto bail; + + if (gvar->offset_to_data > directory->length) + goto bail; + + /* Figure out the size required for the offset array. Note that + there is one extra offset at the end of the array to mark the + size of the last glyph. */ + + if (gvar->flags & 1) + /* Offsets are long words. */ + off_size = sizeof (uint32_t) * (gvar->glyph_count + 1); + else + /* Offsets are words. */ + off_size = sizeof (uint16_t) * (gvar->glyph_count + 1); + + /* Now figure out the size of the shared coordinates. */ + coordinate_size = (gvar->shared_coord_count * gvar->axis_count + * sizeof (uint16_t)); + + /* And the size of the glyph variation data. */ + data_size = directory->length - gvar->offset_to_data; + + /* Wraparound. */ + if (data_size > directory->length) + goto bail; + + /* Figure out how big gvar needs to be. */ + if (INT_ADD_WRAPV (sizeof *gvar, coordinate_size, &min_bytes) + || INT_ADD_WRAPV (min_bytes, off_size, &min_bytes) + || INT_ADD_WRAPV (min_bytes, data_size, &min_bytes)) + goto bail; + + /* Now allocate enough for all of this extra data. */ + gvar = xrealloc (gvar, min_bytes); + + /* Start reading offsets. */ + + if (!(gvar->flags & 1)) + { + gvar->u.offset_word = (uint16_t *) (gvar + 1); + rc = read (fd, gvar->u.offset_word, off_size); + if (rc != off_size) + goto bail; + + for (i = 0; i <= gvar->glyph_count; ++i) + sfnt_swap16 (&gvar->u.offset_word[i]); + } + else + { + gvar->u.offset_long = (uint32_t *) (gvar + 1); + rc = read (fd, gvar->u.offset_long, off_size); + if (rc != off_size) + goto bail; + + for (i = 0; i <= gvar->glyph_count; ++i) + sfnt_swap32 (&gvar->u.offset_long[i]); + } + + /* Start reading shared coordinates. */ + + gvar->global_coords = ((sfnt_f2dot14 *) ((char *) gvar + off_size)); + + if (gvar->shared_coord_count) + { + if (INT_ADD_WRAPV (gvar->offset_to_coord, directory->offset, + &offset)) + goto bail; + + if (lseek (fd, offset, SEEK_SET) != offset) + goto bail; + + if (read (fd, gvar->global_coords, coordinate_size) + != coordinate_size) + goto bail; + + for (i = 0; i <= coordinate_size / sizeof *gvar->global_coords; ++i) + sfnt_swap16 (&gvar->global_coords[i]); + } + + /* Finally, read the rest of the glyph variation data. */ + gvar->data_size = data_size; + gvar->glyph_variation_data + = (unsigned char *) (gvar->global_coords + + (coordinate_size + / sizeof *gvar->global_coords)); + + if (gvar->data_size) + { + if (INT_ADD_WRAPV (gvar->offset_to_data, directory->offset, + &offset)) + goto bail; + + if (lseek (fd, offset, SEEK_SET) != offset) + goto bail; + + if (read (fd, gvar->glyph_variation_data, + gvar->data_size) != gvar->data_size) + goto bail; + } + + /* Return the read gvar table. */ + return gvar; + + bail: + xfree (gvar); + return NULL; +} + + + +/* 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_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; + + /* Un-normalized coordinates. */ + sfnt_fixed *coords; + + /* Normalized coordinates. */ + sfnt_fixed *norm_coords; +}; + +/* Initialize the specified BLEND with the given FVAR and GVAR + tables. */ + +static void +sfnt_init_blend (struct sfnt_blend *blend, struct sfnt_fvar_table *fvar, + struct sfnt_gvar_table *gvar) +{ + size_t size; + + blend->fvar = fvar; + blend->gvar = gvar; + + /* 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. */ + +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. */ + +static void +sfnt_normalize_blend (struct sfnt_blend *blend) +{ + struct sfnt_variation_axis *axis; + int i; + sfnt_fixed coord; + + /* 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; + } + + /* TODO: process avar tables. */ +} + + + +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. + + If non-NULL, set LOCATION to DATA plus the number of bytes read + upon success. + + Return (uint16_t *) -1 if there are no points at all. + In this case, deltas will apply to all points in the glyph, + and *NPOINTS_RETURN will be UINT16_MAX. + + END is one byte past the last byte in DATA. */ + +static uint16_t * +sfnt_read_packed_points (unsigned char *restrict data, + uint16_t *npoints_return, + unsigned char *restrict end, + unsigned char *restrict *location) +{ + int npoints; + uint16_t *points; + int i, first, control; + + points = NULL; + npoints = 0; + + if (data >= end) + return NULL; + + /* Load the control byte. */ + control = *data++; + + if (!control) + { + *npoints_return = UINT16_MAX; + *location = data; + return (uint16_t *) -1; + } + + /* Now figure out the number of points within. */ + + if (control & 0x80) + { + npoints = control & 0x7f; + npoints <<= 8; + + if (data >= end) + return NULL; + + npoints |= *data++; + } + else + npoints = control; + + /* Start reading points. */ + first = 0; + i = 0; + points = xmalloc (sizeof *points * npoints); + + while (i < npoints) + { + if (data >= end) + goto bail; + + control = *data++; + + if (control & 0x80) + { + /* Next control & 0x7f words are points. */ + + control &= 0x7f; + + while (control != -1 && i < npoints) + { + if (data >= end || data + 1 >= end) + goto bail; + + first += *data++ << 8u; + first += *data++; + points[i] = first; + control -= 1, ++i; + } + } + else + { + /* Next control bytes are points. */ + + while (control != -1 && i < npoints) + { + if (data >= end) + goto bail; + + first += *data++; + points[i] = first; + control -= 1, ++i; + } + } + } + + /* Return the points read. */ + *npoints_return = npoints; + *location = data; + return points; + + bail: + xfree (points); + return NULL; +} + +/* Read and return N packed deltas from DATA. Set *DATA_RETURN to + DATA plus the number of bytes read. + + END is the end of the glyph variation data. Value is an array of N + deltas upon success, and NULL upon failure. */ + +static sfnt_fword * +sfnt_read_packed_deltas (unsigned char *restrict data, + unsigned char *restrict end, + int n, + unsigned char *restrict *data_return) +{ + sfnt_fword *deltas; + int i, count; + unsigned char control; + + if (data >= end) + return NULL; + + deltas = xmalloc (sizeof *deltas * n); + i = 0; + + while (i < n) + { + if (data >= end) + goto fail; + + control = *data++; + count = control & 0x3f; + + while (count != -1 && i < n) + { + if (control & 0x80) + deltas[i++] = 0; + else if (control & 0x40) + { + if (data + 1 >= end) + goto fail; + + deltas[i] = (signed char) *data++; + deltas[i] *= 65536; + deltas[i++] |= *data++; + } + else + { + if (data >= end) + goto fail; + + deltas[i++] = (signed char) *data++; + } + + --count; + } + } + + *data_return = data; + return deltas; + + fail: + xfree (deltas); + 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. */ + +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; + } + + /* 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. + + 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. */ + +static int +sfnt_vary_glyph (struct sfnt_blend *blend, sfnt_glyph id, + struct sfnt_glyph *glyph) +{ + 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 *dx, *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; + + 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; + + coords = xmalloc (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; + + for (i = 0; i < ntuples; ++i) + { + 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 & 0x1000) + { + 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. */ + + if (glyph->simple->number_of_points > 65535) + abort (); + + /* 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; + } + + /* TODO: apply metrics variations. */ + break; + + default: + dx = sfnt_read_packed_deltas (data, end, point_count, &data); + dy = sfnt_read_packed_deltas (data, end, point_count, &data); + + if (!dx || !dy) + goto fail3; + + /* Deltas are only applied for each point number read. */ + + if (!original_x) + { + 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; + memcpy (original_x, glyph->simple->x_coordinates, + (sizeof *original_x + * glyph->simple->number_of_points)); + memcpy (original_y, glyph->simple->y_coordinates, + (sizeof *original_y + * glyph->simple->number_of_points)); + } + + memset (touched, 0, (sizeof *touched + * glyph->simple->number_of_points)); + + for (i = 0; i < point_count; ++i) + { + /* 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; + } + + sfnt_infer_deltas (glyph, touched, original_x, + original_y); + + /* TODO: apply metrics variations. */ + break; + } + + xfree (dx); + xfree (dy); + + if (local_points != (uint16_t *) -1) + xfree (local_points); + } + + /* Return success. */ + + xfree (touched); + xfree (coords); + xfree (original_x); + + if (points != (uint16_t *) -1) + xfree (points); + + return 0; + + fail3: + xfree (dx); + xfree (dy); + xfree (local_points); + fail1: + xfree (touched); + xfree (coords); + xfree (original_x); + + if (points != (uint16_t *) -1) + xfree (points); + + return 1; +} + +#endif /* TEST */ + + + +#ifdef TEST + +struct sfnt_test_dcontext +{ + /* Context for sfnt_test_get_glyph. */ + struct sfnt_glyf_table *glyf; + struct sfnt_loca_table_short *loca_short; + struct sfnt_loca_table_long *loca_long; +}; + +/* Global context for test functions. Height of glyph. */ +static sfnt_fixed sfnt_test_max; + +static void +sfnt_test_move_to (struct sfnt_point point, void *dcontext) +{ + printf ("move_to: %g, %g\n", sfnt_coerce_fixed (point.x), + sfnt_coerce_fixed (point.y)); +} + +static void +sfnt_test_line_to (struct sfnt_point point, void *dcontext) +{ + printf ("line_to: %g, %g\n", sfnt_coerce_fixed (point.x), + sfnt_coerce_fixed (point.y)); +} + +static void +sfnt_test_curve_to (struct sfnt_point control, + struct sfnt_point endpoint, + void *dcontext) +{ + printf ("curve_to: %g, %g - %g, %g\n", + sfnt_coerce_fixed (control.x), + sfnt_coerce_fixed (control.y), + sfnt_coerce_fixed (endpoint.x), + sfnt_coerce_fixed (endpoint.y)); +} + +static struct sfnt_glyph * +sfnt_test_get_glyph (sfnt_glyph glyph, void *dcontext, + bool *need_free) +{ + struct sfnt_test_dcontext *tables; + + tables = dcontext; + *need_free = true; + + return sfnt_read_glyph (glyph, tables->glyf, + tables->loca_short, + tables->loca_long); +} + +static void +sfnt_test_free_glyph (struct sfnt_glyph *glyph, void *dcontext) +{ + sfnt_free_glyph (glyph); +} + +static void +sfnt_test_span (struct sfnt_edge *edge, sfnt_fixed y, + void *dcontext) +{ +#if 1 + printf ("/* span at %g */\n", sfnt_coerce_fixed (y)); + for (; edge; edge = edge->next) + { + if (y >= edge->bottom && y < edge->top) + printf ("ctx.fillRect (%g, %g, 1, 1); " + "/* %g top: %g bot: %g stepx: %g winding: %d */\n", + sfnt_coerce_fixed (edge->x), + sfnt_coerce_fixed (sfnt_test_max - y), + sfnt_coerce_fixed (y), + sfnt_coerce_fixed (edge->top), + sfnt_coerce_fixed (edge->bottom), + sfnt_coerce_fixed (edge->step_x), + edge->winding); + else + printf ("STRIPPED BAD SPAN!!! %g %g %"PRIi32 + " %"PRIi32" (winding: %d)\n", + sfnt_coerce_fixed (edge->top), + sfnt_coerce_fixed (edge->bottom), + edge->top, y, edge->winding); + } +#elif 0 + int winding; + short x, dx; + + winding = 0; + x = 0; + + for (; edge; edge = edge->next) + { + dx = (edge->x >> 16) - x; + x = edge->x >> 16; + + for (; dx > 0; --dx) + putc (winding ? '.' : ' ', stdout); + + winding = !winding; + } + + putc ('\n', stdout); +#elif 0 + for (; edge; edge = edge->next) + printf ("%g-", sfnt_coerce_fixed (edge->x)); + puts (""); +#endif +} + +static void +sfnt_test_edge_ignore (struct sfnt_edge *edges, size_t num_edges, + void *dcontext) +{ + +} + +/* The same debugger stuff is used here. */ +static void sfnt_setup_debugger (void); + +/* The debugger's X display. */ +static Display *display; + +/* The debugger window. */ +static Window window; + +/* The GC. */ +static GC point_gc, background_gc; + +static void +sfnt_test_edges (struct sfnt_edge *edges, size_t num_edges) +{ + static sfnt_fixed y; + size_t i; + + for (i = 0; i < num_edges; ++i) + { + if (y >= edges[i].bottom && y < edges[i].top) + { + XDrawPoint (display, window, point_gc, + edges[i].x / 65536, 100 - (y / 65536)); + printf ("sfnt_test_edges: %d %d\n", + edges[i].x / 65536, 100 - (y / 65536)); + } + } + + y += SFNT_POLY_STEP; + + for (i = 0; i < num_edges; ++i) + sfnt_step_edge (&edges[i]); +} + +static void +sfnt_debug_edges (struct sfnt_edge *edges, size_t num_edges) +{ + XEvent event; + + sfnt_setup_debugger (); + + while (true) + { + XNextEvent (display, &event); + + switch (event.type) + { + case KeyPress: + XDestroyWindow (display, window); + XCloseDisplay (display); + exit (0); + break; + + case Expose: + + while (true) + { + sfnt_test_edges (edges, num_edges); + XFlush (display); + usleep (50000); + } + + break; + } + } +} + +static void +sfnt_test_edge (struct sfnt_edge *edges, size_t num_edges, + void *dcontext) +{ + size_t i; + + printf ("built %zu edges\n", num_edges); + + for (i = 0; i < num_edges; ++i) + { + printf ("/* edge x, top, bot: %g, %g - %g. winding: %d */\n" + "/* edge step_x: %g */\n", + sfnt_coerce_fixed (edges[i].x), + sfnt_coerce_fixed (edges[i].top), + sfnt_coerce_fixed (edges[i].bottom), + edges[i].winding, + sfnt_coerce_fixed (edges[i].step_x)); +#ifdef TEST_VERTEX + printf ("ctx.fillRect (%g, %g, 1, 1);\n", + sfnt_coerce_fixed (edges[i].x), + sfnt_coerce_fixed (sfnt_test_max + - edges[i].y)); +#else + printf ("ctx.fillRect (%g, %g, 1, 1);\n", + sfnt_coerce_fixed (edges[i].x), + sfnt_coerce_fixed (sfnt_test_max + - edges[i].bottom)); +#endif + } + + if (getenv ("SFNT_DEBUG_STEP")) + { + if (!fork ()) + sfnt_debug_edges (edges, num_edges); + } + + printf ("==end of edges==\n"); + + sfnt_poly_edges (edges, num_edges, sfnt_test_span, NULL); +} + +static void +sfnt_x_raster (struct sfnt_raster **rasters, + int *advances, + int nrasters, + struct sfnt_hhea_table *hhea, + sfnt_fixed scale) +{ + Display *display; + Window window; + Pixmap *pixmaps; + Picture *glyphs, drawable, solid; + int event_base, error_base; + int major, minor, *depths, count; + XRenderPictFormat *format, *glyph_format; + Visual *visual; + XImage image; + GC gc; + XGCValues gcvalues; + XEvent event; + XRenderColor white, black; + int i, ascent, origin, x, y; + Font font; + + if (!nrasters) + exit (0); + + display = XOpenDisplay (NULL); + + if (!display) + exit (0); + + if (!XRenderQueryExtension (display, &event_base, &error_base) + || !XRenderQueryVersion (display, &major, &minor)) + exit (0); + + if (major == 0 && minor < 10) + exit (0); + + window = XCreateSimpleWindow (display, DefaultRootWindow (display), + 0, 0, 100, 150, 0, 0, + WhitePixel (display, + DefaultScreen (display))); + XSelectInput (display, window, ExposureMask); + XMapWindow (display, window); visual = DefaultVisual (display, DefaultScreen (display)); format = XRenderFindVisualFormat (display, visual); @@ -16262,15 +17909,22 @@ main (int argc, char **argv) struct sfnt_prep_table *prep; struct sfnt_graphics_state state; struct sfnt_instructed_outline *value; + struct sfnt_fvar_table *fvar; + struct sfnt_gvar_table *gvar; sfnt_fixed scale; char *fancy; int *advances; struct sfnt_raster **rasters; size_t length; + char *axis_name; + struct sfnt_instance *instance; + struct sfnt_blend blend; if (argc < 2) return 1; + instance = NULL; + if (!strcmp (argv[1], "--check-interpreter")) { interpreter = sfnt_make_test_interpreter (); @@ -16324,7 +17978,7 @@ main (int argc, char **argv) font = sfnt_read_table_directory (fd); } - if (!font) + if (!font || font == (struct sfnt_offset_subtable *) -1) { close (fd); return 1; @@ -16375,8 +18029,8 @@ main (int argc, char **argv) return 1; } -#define FANCY_PPEM 25 -#define EASY_PPEM 25 +#define FANCY_PPEM 12 +#define EASY_PPEM 12 interpreter = NULL; head = sfnt_read_head_table (fd, font); @@ -16388,16 +18042,18 @@ main (int argc, char **argv) cvt = sfnt_read_cvt_table (fd, font); fpgm = sfnt_read_fpgm_table (fd, font); prep = sfnt_read_prep_table (fd, font); + fvar = sfnt_read_fvar_table (fd, font); + gvar = sfnt_read_gvar_table (fd, font); hmtx = NULL; exec_prep = prep; exec_fpgm = fpgm; - - if (getenv ("SFNT_FANCY_TEST")) + fancy = getenv ("SFNT_FANCY_TEST"); + + if (fancy) { loca_long = NULL; loca_short = NULL; - fancy = getenv ("SFNT_FANCY_TEST"); length = strlen (fancy); scale = sfnt_div_fixed (FANCY_PPEM, head->units_per_em); @@ -16659,6 +18315,82 @@ main (int argc, char **argv) (int) hhea->caret_slope_rise, (int) hhea->caret_slope_run); + 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 < 0 || i >= fvar->instance_count) + goto free_lab; + + instance = &fvar->instance[i]; + } + } + + 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? "); @@ -16696,6 +18428,14 @@ main (int argc, char **argv) dcontext.loca_short = loca_short; dcontext.loca_long = loca_long; + if (glyph->simple && instance && gvar) + { + printf ("applying variations to simple glyph...\n"); + + if (sfnt_vary_glyph (&blend, code, glyph)) + printf ("variation failed!\n"); + } + if (sfnt_decompose_glyph (glyph, sfnt_test_move_to, sfnt_test_line_to, sfnt_test_curve_to, @@ -16871,6 +18611,8 @@ main (int argc, char **argv) } } + free_lab: + xfree (font); for (i = 0; i < table->num_subtables; ++i) @@ -16893,6 +18635,11 @@ main (int argc, char **argv) xfree (fpgm); xfree (interpreter); xfree (prep); + xfree (fvar); + xfree (gvar); + + if (instance && gvar) + sfnt_free_blend (&blend); return 0; } diff --git a/src/sfnt.h b/src/sfnt.h index 83d34bfb757..82fa343f51d 100644 --- a/src/sfnt.h +++ b/src/sfnt.h @@ -53,6 +53,9 @@ enum sfnt_table SFNT_TABLE_CVT , SFNT_TABLE_FPGM, SFNT_TABLE_PREP, + SFNT_TABLE_FVAR, + SFNT_TABLE_GVAR, + SFNT_TABLE_CVAR, }; #define SFNT_ENDOF(type, field, type1) \ @@ -61,7 +64,14 @@ enum sfnt_table /* Each of these structures must be aligned so that no compiler will ever generate padding bytes on platforms where the alignment requirements for uint32_t and uint16_t are no larger than 4 and 2 - bytes respectively. */ + bytes respectively. + + Pointer types are assumed to impose an alignmnent requirement no + less than that of uint32_t. + + If a table has more than one kind of variable-length subtable array + at the end, make sure to pad subsequent subtables + appropriately. */ struct sfnt_offset_subtable { @@ -568,25 +578,25 @@ struct sfnt_simple_glyph size_t number_of_points; /* Array containing the last points of each contour. */ - uint16_t *end_pts_of_contours; + uint16_t *restrict end_pts_of_contours; /* Total number of bytes needed for instructions. */ uint16_t instruction_length; /* Instruction data. */ - uint8_t *instructions; + uint8_t *restrict instructions; /* Array of flags. */ - uint8_t *flags; + uint8_t *restrict flags; /* Array of X coordinates. */ - int16_t *x_coordinates; + int16_t *restrict x_coordinates; /* Array of Y coordinates. */ - int16_t *y_coordinates; + int16_t *restrict y_coordinates; /* Pointer to the end of that array. */ - int16_t *y_coordinates_end; + int16_t *restrict y_coordinates_end; }; struct sfnt_compound_glyph_component -- 2.39.2