From 85dd157c38dc8e52367a3a6e574c512d2b7f7c9b Mon Sep 17 00:00:00 2001 From: Po Lu Date: Thu, 9 Feb 2023 20:13:42 +0800 Subject: [PATCH] Implement instructing compound glyphs * src/sfnt.c (sfnt_read_simple_glyph, sfnt_read_compound_glyph) (sfnt_read_glyph): Take size_t offsets. (struct sfnt_compound_glyph_context) (sfnt_expand_compound_glyph_context) (sfnt_decompose_compound_glyph): Take size_t contour offsets. (sfnt_decompose_glyph): Always close contour even if the first point isn't on-curve. (sfnt_build_outline_edges): Fix coding style. (sfnt_interpret_iup): Skip phantom points during IUP. (sfnt_decompose_instructed_outline): Clarify documentation. Always close contour even if the first point isn't on-curve. (struct sfnt_test_dcontext, sfnt_test_move_to, sfnt_test_line_to) (sfnt_test_curve_to, sfnt_transform_f26dot6, sfnt_test_get_glyph) (sfnt_test_free_glyph, sfnt_test_span, sfnt_test_edge_ignore) (sfnt_interpret_compound_glyph_2, sfnt_test_edges, main): Update tests. * src/sfnt.h: Export new function. * src/sfntfont.c (sfntfont_get_glyph_outline): Handle compound glyphs. (sfntfont_measure_instructed_pcm, sfntfont_measure_pcm) (sfntfont_draw): Update accordingly. --- src/sfnt.c | 1193 ++++++++++++++++++++++++++++++++++++++++-------- src/sfnt.h | 22 + src/sfntfont.c | 46 +- 3 files changed, 1059 insertions(+), 202 deletions(-) diff --git a/src/sfnt.c b/src/sfnt.c index 1a5bac22582..a800f0ef7a0 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -1590,7 +1590,7 @@ sfnt_read_glyf_table (int fd, struct sfnt_offset_subtable *subtable) static void sfnt_read_simple_glyph (struct sfnt_glyph *glyph, struct sfnt_glyf_table *glyf, - ptrdiff_t offset) + size_t offset) { struct sfnt_simple_glyph *simple; ssize_t min_size, min_size_2; @@ -1862,7 +1862,7 @@ sfnt_read_simple_glyph (struct sfnt_glyph *glyph, static void sfnt_read_compound_glyph (struct sfnt_glyph *glyph, struct sfnt_glyf_table *glyf, - ptrdiff_t offset) + size_t offset) { uint16_t flags, instruction_length, words[2], words4[4]; size_t required_bytes, num_components, i; @@ -2105,7 +2105,7 @@ sfnt_read_glyph (sfnt_glyph glyph_code, struct sfnt_loca_table_long *loca_long) { struct sfnt_glyph glyph, *memory; - ptrdiff_t offset, next_offset; + size_t offset, next_offset; /* Check the glyph code is within bounds. */ if (glyph_code > 65535) @@ -2301,7 +2301,8 @@ sfnt_transform_coordinates (struct sfnt_compound_glyph_component *component, struct sfnt_compound_glyph_context { - /* Array of points. */ + /* Arrays of points. The underlying type is actually sfnt_f26dot6 + when instructing a compound glyph. */ sfnt_fixed *x_coordinates, *y_coordinates; /* Array of flags for the points. */ @@ -2311,7 +2312,7 @@ struct sfnt_compound_glyph_context size_t num_points, points_size; /* Array of contour end points. */ - ptrdiff_t *contour_end_points; + size_t *contour_end_points; /* Number of elements in and the size of that array. */ size_t num_end_points, end_points_size; @@ -2331,7 +2332,7 @@ sfnt_expand_compound_glyph_context (struct sfnt_compound_glyph_context *context, size_t number_of_points, sfnt_fixed **x_base, sfnt_fixed **y_base, unsigned char **flags_base, - ptrdiff_t **contour_base) + size_t **contour_base) { size_t size_bytes; @@ -2433,12 +2434,12 @@ sfnt_decompose_compound_glyph (struct sfnt_glyph *glyph, bool need_free; struct sfnt_compound_glyph_component *component; sfnt_fixed x, y, xtemp, ytemp; - ptrdiff_t point, point2, index; + size_t point, point2, index; uint16_t last_point, number_of_contours; sfnt_fixed *x_base, *y_base; - ptrdiff_t *contour_base; + size_t *contour_base; unsigned char *flags_base; - ptrdiff_t base_index, contour_start; + size_t base_index, contour_start; bool defer_offsets; /* Set up the base index. This is the index from where on point @@ -2546,8 +2547,8 @@ sfnt_decompose_compound_glyph (struct sfnt_glyph *glyph, /* Get the points and use them to compute the offsets. */ xtemp = context->x_coordinates[point]; ytemp = context->y_coordinates[point]; - x = (xtemp - subglyph->simple->x_coordinates[point2]) * 65536; - y = (ytemp - subglyph->simple->y_coordinates[point2]) * 65536; + x = (xtemp - subglyph->simple->x_coordinates[point2] * 65536); + y = (ytemp - subglyph->simple->y_coordinates[point2] * 65536); } else { @@ -2713,8 +2714,8 @@ sfnt_lerp_half (struct sfnt_point *control1, struct sfnt_point *control2, spline. If GLYPH is compound, use GET_GLYPH to obtain subglyphs. PROC must - return whether or not FREE_PROC will be called with the glyph after - sfnt_decompose_glyph is done with it. + return whether or not FREE_GLYPH will be called with the glyph + after sfnt_decompose_glyph is done with it. All functions will be called with DCONTEXT as an argument. @@ -2832,27 +2833,31 @@ sfnt_decompose_glyph (struct sfnt_glyph *glyph, /* Restore here after the for loop increased it. */ here --; - if (glyph->simple->flags[start] & 01) /* On Curve */ - { - pen.x = glyph->simple->x_coordinates[start] * 65536; - pen.y = glyph->simple->y_coordinates[start] * 65536; + /* Previously, this would check whether or not start is + an ``on curve'' point, but that is not necessary. - /* See if the last point (in this case, `here') was - on the curve. If it wasn't, then curve from - there to here. */ - if (!(glyph->simple->flags[here] & 01)) - { - control1.x - = glyph->simple->x_coordinates[here] * 65536; - control1.y - = glyph->simple->y_coordinates[here] * 65536; - curve_to (control1, pen, dcontext); - } - else - /* Otherwise, this is an ordinary line from there - to here. */ - line_to (pen, dcontext); + If a contour is not closed and the edge building + process skips the second to last vertex, then the + outline can end up with missing edges. */ + + pen.x = glyph->simple->x_coordinates[start] * 65536; + pen.y = glyph->simple->y_coordinates[start] * 65536; + + /* See if the last point (in this case, `here') was + on the curve. If it wasn't, then curve from + there to here. */ + if (!(glyph->simple->flags[here] & 01)) + { + control1.x + = glyph->simple->x_coordinates[here] * 65536; + control1.y + = glyph->simple->y_coordinates[here] * 65536; + curve_to (control1, pen, dcontext); } + else + /* Otherwise, this is an ordinary line from there + to here. */ + line_to (pen, dcontext); /* Restore here to where it was earlier. */ here++; @@ -2968,25 +2973,28 @@ sfnt_decompose_glyph (struct sfnt_glyph *glyph, /* Restore here after the for loop increased it. */ here --; - if (context.flags[start] & 01) /* On Curve */ - { - pen.x = context.x_coordinates[start]; - pen.y = context.y_coordinates[start]; + /* Previously, this would check whether or not start is an + ``on curve'' point, but that is not necessary. - /* See if the last point (in this case, `here') was - on the curve. If it wasn't, then curve from - there to here. */ - if (!(context.flags[here] & 01)) - { - control1.x = context.x_coordinates[here]; - control1.y = context.y_coordinates[here]; - curve_to (control1, pen, dcontext); - } - else - /* Otherwise, this is an ordinary line from there - to here. */ - line_to (pen, dcontext); + If a contour is not closed and the edge building process + skips the second to last vertex, then the outline can end + up with missing edges. */ + + pen.x = context.x_coordinates[start]; + pen.y = context.y_coordinates[start]; + + /* See if the last point (in this case, `here') was on the + curve. If it wasn't, then curve from there to here. */ + if (!(context.flags[here] & 01)) + { + control1.x = context.x_coordinates[here]; + control1.y = context.y_coordinates[here]; + curve_to (control1, pen, dcontext); } + else + /* Otherwise, this is an ordinary line from there + to here. */ + line_to (pen, dcontext); /* Restore here to where it was earlier. */ here++; @@ -3672,7 +3680,8 @@ sfnt_build_outline_edges (struct sfnt_glyph_outline *outline, y = sfnt_poly_grid_ceil (bot); /* If rounding would make the edge not cover any area, skip this - edge. */ + edge. */ + if (y >= edges[edge].top) continue; @@ -4125,7 +4134,7 @@ sfnt_read_hmtx_table (int fd, struct sfnt_offset_subtable *subtable, metrics could be found, else 1. If PIXEL_SIZE is -1, do not perform any scaling on the glyph - metrics. + metrics; HEAD need not be specified in that case. HMTX, HHEA, HEAD and MAXP should be the hmtx, hhea, head, and maxp tables of the font respectively. */ @@ -9554,6 +9563,13 @@ sfnt_interpret_iup (struct sfnt_interpreter *interpreter, sfnt_interpret_iup_1 (interpreter, first_point, end, opcode, mask); point = end + 1; + + /* Skip the subsequent phantom points, which may end up + intermixed with contours inside a compound glyph. */ + + while (point < interpreter->glyph_zone->num_points + && interpreter->glyph_zone->flags[point] & SFNT_POINT_PHANTOM) + point++; } } @@ -10470,7 +10486,7 @@ sfnt_interpret_control_value_program (struct sfnt_interpreter *interpreter, comprising the spline. Call each of those functions with 16.16 fixed point coordinates. - All functions will be called with DCONTEXT as an argument. + Call all functions with DCONTEXT as an argument. The winding rule used to fill the resulting lines is described in chapter 2 of the TrueType reference manual, under the heading @@ -10578,29 +10594,42 @@ sfnt_decompose_instructed_outline (struct sfnt_instructed_outline *outline, /* Restore here after the for loop increased it. */ here --; - if (outline->flags[start] & 01) /* On Curve */ - { - pen.x = outline->x_points[start] * 1024; - pen.y = outline->y_points[start] * 1024; + /* Previously, this would check whether or not start is an + ``on curve'' point, but that is not necessary. - /* See if the last point (in this case, `here') was - on the curve. If it wasn't, then curve from - there to here. */ - if (!(outline->flags[here] & 01)) - { - control1.x = outline->x_points[here] * 1024; - control1.y = outline->y_points[here] * 1024; - curve_to (control1, pen, dcontext); - } - else - /* Otherwise, this is an ordinary line from there - to here. */ - line_to (pen, dcontext); + If a contour is not closed and the edge building process + skips the second to last vertex, then the outline can end + up with missing edges. */ + + pen.x = outline->x_points[start] * 1024; + pen.y = outline->y_points[start] * 1024; + + /* See if the last point (in this case, `here') was + on the curve. If it wasn't, then curve from + there to here. */ + if (!(outline->flags[here] & 01)) + { + control1.x = outline->x_points[here] * 1024; + control1.y = outline->y_points[here] * 1024; + curve_to (control1, pen, dcontext); } + else + /* Otherwise, this is an ordinary line from there + to here. */ + line_to (pen, dcontext); /* Restore here to where it was earlier. */ here++; } + + /* here may be a phantom point when outlining a compound glyph, + as they can have phantom points mixed in with contours. + + When that happens, skip past all the phantom points. */ + + while (here < outline->num_points + && outline->flags[here] & SFNT_POINT_PHANTOM) + here++; } return 0; @@ -10713,7 +10742,8 @@ sfnt_interpret_simple_glyph (struct sfnt_glyph *glyph, struct sfnt_instructed_outline **value) { size_t zone_size, temp, outline_size, i; - struct sfnt_interpreter_zone *volatile zone; + struct sfnt_interpreter_zone *zone; + struct sfnt_interpreter_zone *volatile preserved_zone; sfnt_f26dot6 phantom_point_1_x; sfnt_f26dot6 phantom_point_1_y; sfnt_f26dot6 phantom_point_2_x; @@ -10806,6 +10836,9 @@ sfnt_interpret_simple_glyph (struct sfnt_glyph *glyph, /* Set flags. */ zone->flags[i] = (glyph->simple->flags[i] & ~SFNT_POINT_TOUCHED_BOTH); + + /* Make sure to clear the phantom points flag. */ + zone->flags[i] &= ~SFNT_POINT_PHANTOM; } /* Load phantom points. */ @@ -10815,8 +10848,8 @@ sfnt_interpret_simple_glyph (struct sfnt_glyph *glyph, zone->y_current[i + 1] = phantom_point_2_x; /* Load phantom point flags. */ - zone->flags[i] = 0; - zone->flags[i + 1] = 0; + zone->flags[i] = SFNT_POINT_PHANTOM; + zone->flags[i + 1] = SFNT_POINT_PHANTOM; /* Load contour end points. */ for (i = 0; i < zone->num_contours; ++i) @@ -10830,16 +10863,23 @@ sfnt_interpret_simple_glyph (struct sfnt_glyph *glyph, interpreter->num_instructions = glyph->simple->instruction_length; interpreter->glyph_zone = zone; + /* Copy zone over to this volatile variable. */ + preserved_zone = zone; + if (setjmp (interpreter->trap)) { if (zone_was_allocated) - xfree (zone); + xfree (preserved_zone); interpreter->glyph_zone = NULL; return interpreter->trap_reason; } sfnt_interpret_run (interpreter, SFNT_RUN_CONTEXT_GLYPH_PROGRAM); + interpreter->glyph_zone = NULL; + + /* Move preserved_zone back to zone. */ + zone = preserved_zone; /* Now that the program has been run, build the scaled outline. */ @@ -10879,142 +10919,892 @@ sfnt_interpret_simple_glyph (struct sfnt_glyph *glyph, return NULL; } -#endif /* SFNT_ENABLE_HINTING */ - - - -#ifdef TEST +/* Apply the transform in the compound glyph component COMPONENT to + the array of points of length NUM_COORDINATES given as X and Y. -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; -}; + Treat X and Y as arrays of 26.6 fixed point values. -/* Global context for test functions. Height of glyph. */ -static sfnt_fixed sfnt_test_max; + Also, apply the 26.6 fixed point offsets X_OFF and Y_OFF to each X + and Y coordinate. -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)); -} + See sfnt_decompose_compound_glyph for an explanation of why offsets + might be applied here, and not while reading the subglyph + itself. */ static void -sfnt_test_line_to (struct sfnt_point point, void *dcontext) +sfnt_transform_f26dot6 (struct sfnt_compound_glyph_component *component, + sfnt_f26dot6 *restrict x, sfnt_f26dot6 *restrict y, + size_t num_coordinates, + sfnt_f26dot6 x_off, sfnt_f26dot6 y_off) { - printf ("line_to: %g, %g\n", sfnt_coerce_fixed (point.x), - sfnt_coerce_fixed (point.y)); -} + double m1, m2, m3; + double m4, m5, m6; + size_t i; -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)); -} + if (component->flags & 010) /* WE_HAVE_A_SCALE */ + { + for (i = 0; i < num_coordinates; ++i) + { + x[i] *= component->u.scale / 16384.0; + y[i] *= component->u.scale / 16384.0; + x[i] += x_off; + y[i] += y_off; + } + } + else if (component->flags & 0100) /* WE_HAVE_AN_X_AND_Y_SCALE */ + { + for (i = 0; i < num_coordinates; ++i) + { + x[i] *= component->u.a.xscale / 16384.0; + y[i] *= component->u.a.yscale / 16384.0; + x[i] += x_off; + y[i] += y_off; + } + } + else if (component->flags & 0200) /* WE_HAVE_A_TWO_BY_TWO */ + { + /* Apply the specified affine transformation. + A transform looks like: -static struct sfnt_glyph * -sfnt_test_get_glyph (sfnt_glyph glyph, void *dcontext, - bool *need_free) -{ - struct sfnt_test_dcontext *tables; + M1 M2 M3 X + M4 M5 M6 * Y - tables = dcontext; - *need_free = true; + = - return sfnt_read_glyph (glyph, tables->glyf, - tables->loca_short, - tables->loca_long); -} + M1*X + M2*Y + M3*1 = X1 + M4*X + M5*Y + M6*1 = Y1 -static void -sfnt_test_free_glyph (struct sfnt_glyph *glyph, void *dcontext) -{ - sfnt_free_glyph (glyph); -} + (In most transforms, there is another row at the bottom for + mathematical reasons. Since Z1 is always 1.0, the row is + simply implied to be 0 0 1, because 0 * x + 0 * y + 1 * 1 = + 1.0. See the definition of matrix3x3 in image.c for some + more explanations about this.) */ + m1 = component->u.b.xscale / 16384.0; + m2 = component->u.b.scale01 / 16384.0; + m3 = 0; + m4 = component->u.b.scale10 / 16384.0; + m5 = component->u.b.yscale / 16384.0; + m6 = 0; -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->bottom), - sfnt_coerce_fixed (edge->top), - sfnt_coerce_fixed (edge->step_x), - edge->winding); + for (i = 0; i < num_coordinates; ++i) + { + x[i] = m1 * x[i] + m2 * y[i] + m3 * 1; + y[i] = m4 * x[i] + m5 * y[i] + m6 * 1; + x[i] += x_off; + y[i] += y_off; + } } -#elif 0 - int winding; - short x, dx; +} - winding = 0; - x = 0; +/* Internal helper for sfnt_interpret_compound_glyph_3. - for (; edge; edge = edge->next) - { - dx = (edge->x >> 16) - x; - x = edge->x >> 16; + Instruct the compound glyph GLYPH using INTERPRETER after all of + its components have been instructed. - for (; dx > 0; --dx) - putc (winding ? '.' : ' ', stdout); + Use the unscaled METRICS to compute the phantom points of this + glyph. - winding = !winding; - } + CONTEXT contains the points and contours of this compound glyph, + numbered starting from BASE_INDEX and BASE_CONTOUR respectively. - putc ('\n', stdout); -#elif 0 - for (; edge; edge = edge->next) - printf ("%g-", sfnt_coerce_fixed (edge->x)); - puts (""); -#endif -} + Value is NULL upon success, or a description of the error upon + failure. */ -static void -sfnt_test_edge_ignore (struct sfnt_edge *edges, size_t num_edges, - void *dcontext) -{ +static const char * +sfnt_interpret_compound_glyph_2 (struct sfnt_glyph *glyph, + struct sfnt_interpreter *interpreter, + struct sfnt_compound_glyph_context *context, + size_t base_index, size_t base_contour, + struct sfnt_glyph_metrics *metrics) +{ + size_t num_points, num_contours, i; + size_t zone_size, temp; + struct sfnt_interpreter_zone *zone; + struct sfnt_interpreter_zone *volatile preserved_zone; + sfnt_f26dot6 phantom_point_1_x; + sfnt_f26dot6 phantom_point_1_y; + sfnt_f26dot6 phantom_point_2_x; + sfnt_f26dot6 phantom_point_2_y; + volatile bool zone_was_allocated; + int rc; + sfnt_f26dot6 *x_base, *y_base; + size_t *contour_base; + unsigned char *flags_base; -} + /* Figure out how many points and contours there are to + instruct. */ + num_points = context->num_points - base_index; + num_contours = context->num_end_points - base_contour; -/* The same debugger stuff is used here. */ -static void sfnt_setup_debugger (void); + /* Nothing to instruct! */ + if (!num_points && !num_contours) + return NULL; -/* The debugger's X display. */ -static Display *display; + /* Build the zone. First, calculate the size of the zone + structure. */ -/* The debugger window. */ -static Window window; + zone_size = 0; + zone_was_allocated = false; -/* The GC. */ -static GC point_gc, background_gc; + if (INT_MULTIPLY_WRAPV (num_points + 2, + sizeof *zone->x_points * 4, + &temp) + || INT_ADD_WRAPV (temp, zone_size, &zone_size) + || INT_MULTIPLY_WRAPV (num_contours, + sizeof *zone->contour_end_points, + &temp) + || INT_ADD_WRAPV (temp, zone_size, &zone_size) + || INT_MULTIPLY_WRAPV (num_points + 2, + sizeof *zone->flags, + &temp) + || INT_ADD_WRAPV (temp, zone_size, &zone_size) + || INT_ADD_WRAPV (sizeof *zone, zone_size, &zone_size)) + return "Glyph exceeded maximum permissible size"; -static void -sfnt_test_edges (struct sfnt_edge *edges, size_t num_edges) -{ - static sfnt_fixed y; - size_t i; + /* Don't use malloc if possible. */ - for (i = 0; i < num_edges; ++i) + if (zone_size <= 1024 * 16) + zone = alloca (zone_size); + else { - if (y >= edges[i].bottom && y < edges[i].top) + zone = xmalloc (zone_size); + zone_was_allocated = true; + } + + /* Now load the zone with data. */ + zone->num_points = num_points + 2; + zone->num_contours = num_contours; + zone->contour_end_points = (size_t *) (zone + 1); + zone->x_points = (sfnt_f26dot6 *) (zone->contour_end_points + + zone->num_contours); + zone->x_current = zone->x_points + zone->num_points; + zone->y_points = zone->x_current + zone->num_points; + zone->y_current = zone->y_points + zone->num_points; + zone->flags = (unsigned char *) (zone->y_current + + zone->num_points); + + /* Copy and renumber all contour end points to start from + base_index. */ + + for (i = 0; i < zone->num_contours; ++i) + zone->contour_end_points[i] + = (context->contour_end_points[base_contour + i] + - base_index); + + /* Now copy over x_points, x_current, y_points and y_current. */ + + for (i = 0; i < num_points; ++i) + { + zone->x_current[i] = context->x_coordinates[i + base_index]; + zone->x_points[i] = context->x_coordinates[i + base_index]; + } + + /* Compute phantom points. */ + sfnt_compute_phantom_points (glyph, metrics, interpreter->scale, + &phantom_point_1_x, &phantom_point_1_y, + &phantom_point_2_x, &phantom_point_2_y); + + /* Load phantom points. */ + zone->x_points[i] = phantom_point_1_x; + zone->x_points[i + 1] = phantom_point_2_x; + zone->x_current[i] = phantom_point_1_x; + zone->x_current[i + 1] = phantom_point_2_x; + + for (i = 0; i < num_points; ++i) + { + zone->y_current[i] = context->y_coordinates[i + base_index]; + zone->y_points[i] = context->y_coordinates[i + base_index]; + + /* Set flags. */ + zone->flags[i] = (context->flags[i + base_index] + & ~SFNT_POINT_TOUCHED_BOTH); + } + + /* Load phantom points. */ + zone->y_points[i] = phantom_point_1_y; + zone->y_points[i + 1] = phantom_point_2_y; + zone->y_current[i] = phantom_point_1_x; + zone->y_current[i + 1] = phantom_point_2_x; + + /* Load phantom point flags. */ + zone->flags[i] = SFNT_POINT_PHANTOM; + zone->flags[i + 1] = SFNT_POINT_PHANTOM; + + /* Load the compound glyph program. */ + interpreter->IP = 0; + interpreter->SP = interpreter->stack; + interpreter->instructions = glyph->compound->instructions; + interpreter->num_instructions = glyph->compound->instruction_length; + interpreter->glyph_zone = zone; + + /* Copy zone over to this volatile variable. */ + preserved_zone = zone; + + if (setjmp (interpreter->trap)) + { + if (zone_was_allocated) + xfree (preserved_zone); + + interpreter->glyph_zone = NULL; + return interpreter->trap_reason; + } + + sfnt_interpret_run (interpreter, SFNT_RUN_CONTEXT_GLYPH_PROGRAM); + interpreter->glyph_zone = NULL; + + /* Move preserved_zone back to zone. */ + zone = preserved_zone; + + /* Now copy the instructed points back, and add the two phantom + points to the end. */ + + for (i = 0; i < num_points; ++i) + { + context->x_coordinates[base_index + i] = zone->x_current[i]; + context->y_coordinates[base_index + i] = zone->y_current[i]; + } + + /* Grow various arrays to fit the phantom points. */ + rc = sfnt_expand_compound_glyph_context (context, 0, 2, + &x_base, &y_base, + &flags_base, + &contour_base); + + if (rc) + { + if (zone_was_allocated) + xfree (zone); + + return "Failed to expand arrays for phantom points"; + } + + /* Copy over the phantom points. */ + x_base[0] = zone->x_current[num_points - 2]; + x_base[1] = zone->x_current[num_points - 1]; + y_base[0] = zone->y_current[num_points - 2]; + y_base[1] = zone->y_current[num_points - 1]; + flags_base[0] = zone->flags[num_points - 2]; + flags_base[1] = zone->flags[num_points - 1]; + + /* Free the zone if needed. */ + if (zone_was_allocated) + xfree (zone); + + return NULL; +} + +/* Internal helper for sfnt_interpret_compound_glyph. + RECURSION_COUNT is the number of times this function has called itself. + OFF_X and OFF_Y are the offsets to apply to the glyph outline. + + METRICS are the unscaled metrics of this compound glyph. + + Other arguments mean the same as they do in + `sfnt_interpret_compound_glyph'. */ + +static const char * +sfnt_interpret_compound_glyph_1 (struct sfnt_glyph *glyph, + struct sfnt_interpreter *interpreter, + struct sfnt_graphics_state *state, + struct sfnt_compound_glyph_context *context, + sfnt_get_glyph_proc get_glyph, + sfnt_free_glyph_proc free_glyph, + struct sfnt_hmtx_table *hmtx, + struct sfnt_hhea_table *hhea, + struct sfnt_maxp_table *maxp, + struct sfnt_glyph_metrics *metrics, + sfnt_fixed off_x, sfnt_fixed off_y, + int recursion_count, + void *dcontext) +{ + struct sfnt_glyph *subglyph; + int i, j, rc; + const char *error; + bool need_free; + struct sfnt_compound_glyph_component *component; + sfnt_f26dot6 x, y, xtemp, ytemp; + size_t point, point2; + size_t last_point, number_of_contours; + sfnt_f26dot6 *x_base, *y_base; + size_t *contour_base; + unsigned char *flags_base; + size_t base_index, contour_start, base_contour; + bool defer_offsets; + struct sfnt_instructed_outline *value; + struct sfnt_glyph_metrics sub_metrics; + + /* Set up the base index. This is the index from where on point + renumbering starts. + + In other words, point 0 in this glyph will be 0 + base_index, + point 1 will be 1 + base_index, and so on. */ + base_index = context->num_points; + + /* And this is the index of the first contour in this glyph. */ + base_contour = context->num_end_points; + + /* Prevent infinite loops. */ + if (recursion_count > 12) + return "Overly deep recursion in compound glyph data"; + + /* Don't defer offsets. */ + defer_offsets = false; + + /* Pacify -Wmaybe-uninitialized. */ + point = point2 = 0; + + for (j = 0; j < glyph->compound->num_components; ++j) + { + /* Look up the associated subglyph. */ + component = &glyph->compound->components[j]; + subglyph = get_glyph (component->glyph_index, + dcontext, &need_free); + + if (!subglyph) + return "Failed to obtain component glyph"; + + /* Record the size of the point array before expansion. This + will be the base to apply to all points coming from this + subglyph. */ + contour_start = context->num_points; + + /* Compute the offset for the component. */ + if (component->flags & 02) /* ARGS_ARE_XY_VALUES */ + { + /* Component offsets are X/Y values as opposed to points + GLYPH. */ + + if (!(component->flags & 01)) /* ARG_1_AND_2_ARE_WORDS */ + { + /* X and Y are signed bytes. */ + x = component->argument1.b * 64; + y = component->argument2.b * 64; + } + else + { + /* X and Y are signed words. */ + x = component->argument1.d * 64; + y = component->argument2.d * 64; + } + + /* Now convert X and Y into device coordinates. */ + x = sfnt_mul_f26dot6_fixed (x, interpreter->scale); + y = sfnt_mul_f26dot6_fixed (y, interpreter->scale); + + /* If there is some kind of scale and component offsets are + scaled, then apply the transform to the offset. */ + if (component->flags & 04000) /* SCALED_COMPONENT_OFFSET */ + sfnt_transform_f26dot6 (component, &x, &y, 1, + 0, 0); + + if (component->flags & 04) /* ROUND_XY_TO_GRID */ + { + x = sfnt_round_f26dot6 (x); + y = sfnt_round_f26dot6 (y); + } + } + else + { + /* The offset is determined by matching a point location in + a preceeding component with a point location in the + current component. The index of the point in the + previous component can be determined by adding + component->argument1.a or component->argument1.c to + point. argument2 contains the index of the point in the + current component. */ + + if (!(component->flags & 01)) /* ARG_1_AND_2_ARE_WORDS */ + { + point = base_index + component->argument1.a; + point2 = component->argument2.a; + } + else + { + point = base_index + component->argument1.c; + point2 = component->argument2.c; + } + + /* Now, check that the anchor point specified lies inside + the glyph. */ + + if (point >= contour_start) + { + if (need_free) + free_glyph (subglyph, dcontext); + + return "Invalid anchor point"; + } + + if (!subglyph->compound) + { + if (point2 >= subglyph->simple->number_of_points) + { + if (need_free) + free_glyph (subglyph, dcontext); + + return "Invalid anchored point"; + } + + /* Get the points and use them to compute the offsets. */ + xtemp = context->x_coordinates[point]; + ytemp = context->y_coordinates[point]; + x = (xtemp - subglyph->simple->x_coordinates[point2] * 64); + y = (ytemp - subglyph->simple->y_coordinates[point2] * 64); + } + else + { + /* First, set offsets to 0, because it is not yet + possible to determine the position of the anchor + point in the child. */ + x = 0; + y = 0; + + /* Set a flag which indicates that offsets must be + resolved from the child glyph after it is loaded, but + before it is incorporated into the parent glyph. */ + defer_offsets = true; + } + } + + /* Obtain the glyph metrics. If doing so fails, then cancel + decomposition. */ + + if (sfnt_lookup_glyph_metrics (component->glyph_index, + -1, &sub_metrics, + hmtx, hhea, NULL, maxp)) + { + if (need_free) + free_glyph (subglyph, dcontext); + + return "Failed to obtain component metrics"; + } + + if (subglyph->simple) + { + /* Simple subglyph. Copy over the points and contours, + then transform and instruct them. + + Skip this step for glyphs without contours. */ + + if (subglyph->number_of_contours) + { + /* Now instruct the simple glyph, and copy it over, + including the two phantom points at the end. */ + interpreter->state = *state; + error = sfnt_interpret_simple_glyph (subglyph, interpreter, + &sub_metrics, &value); + + /* Cancel instructing if an error occurs. */ + + if (error) + { + if (need_free) + free_glyph (subglyph, dcontext); + + return error; + } + + /* Figure out how many more points and contours are + needed. Here, last_point is not the end of the + glyph's contours, as two phantom points are + included. */ + last_point = value->num_points; + number_of_contours = value->num_contours; + + /* Grow various arrays. */ + rc = sfnt_expand_compound_glyph_context (context, + /* Number of + new contours + required. */ + number_of_contours, + /* Number of new + points + required. */ + last_point, + &x_base, + &y_base, + &flags_base, + &contour_base); + if (rc) + { + xfree (value); + + if (need_free) + free_glyph (subglyph, dcontext); + + return "Failed to grow arrays"; + } + + /* Copy the values in VALUE into the context and free + VALUE, including phantom points. */ + + for (i = 0; i < last_point; ++i) + { + x_base[i] = value->x_points[i] + off_x + x; + y_base[i] = value->y_points[i] + off_y + y; + flags_base[i] = value->flags[i]; + } + + /* Copy over the contours. */ + for (i = 0; i < number_of_contours; ++i) + contour_base[i] = (contour_start + + value->contour_end_points[i]); + + xfree (value); + + /* Apply the transform to the points. */ + sfnt_transform_f26dot6 (component, x_base, y_base, + last_point, 0, 0); + } + } + else + { + /* Compound subglyph. Decompose and instruct the glyph + recursively, and then apply the transform. */ + + error = sfnt_interpret_compound_glyph_1 (subglyph, interpreter, + state, + context, get_glyph, + free_glyph, hmtx, hhea, + maxp, &sub_metrics, + off_x + x, off_y + y, + recursion_count + 1, + dcontext); + + if (error) + { + if (need_free) + free_glyph (subglyph, dcontext); + + return error; + } + + /* When an anchor point is being used to translate the + glyph, and the subglyph in question is actually a + compound glyph, it is impossible to know which offset to + use until the compound subglyph has actually been + loaded. + + As a result, the offset is calculated here, using the + points in the loaded child compound glyph. But first, X + and Y must be reset to 0, as otherwise the translation + might be applied twice if defer_offsets is not set. */ + + x = 0; + y = 0; + + if (defer_offsets) + { + /* Renumber the non renumbered point2 to point into the + decomposed component. */ + point2 += contour_start; + + /* Next, check that the non-renumbered point being + anchored lies inside the glyph data that was + decomposed. */ + + if (point2 >= context->num_points) + { + if (need_free) + free_glyph (subglyph, dcontext); + + return "Invalid point2"; + } + + /* Get the points and use them to compute the + offsets. */ + + xtemp = context->x_coordinates[point]; + ytemp = context->y_coordinates[point]; + x = (xtemp - context->x_coordinates[point2]); + y = (ytemp - context->y_coordinates[point2]); + } + + sfnt_transform_f26dot6 (component, + context->x_coordinates + contour_start, + context->y_coordinates + contour_start, + contour_start - context->num_points, + x, y); + } + + /* Finally, free the subglyph. */ + if (need_free) + free_glyph (subglyph, dcontext); + } + + /* Run the program for the entire compound glyph, if any. */ + + if (glyph->compound->instruction_length) + { + interpreter->state = *state; + error = sfnt_interpret_compound_glyph_2 (glyph, interpreter, + context, base_index, + base_contour, + metrics); + } + + return error; +} + +/* Interpret the compound glyph GLYPH using the specified INTERPRETER. + Load each component of the compound glyph GLYPH. CONTEXT should be + a reference to a `struct sfnt_compound_glyph_context' that is on + the stack. + + Use glyph metrics specified in the HMTX, HHEA and MAXP tables, and + the unscaled metrics for GLYPH specified in METRICS. + + If the component is a simple glyph, scale and instruct it + immediately. + + If the component is a compound glyph, then load it recursively + until a predetermined recursion level is exceeded. + + Set INTERPRETER's state to STATE prior to instructing each + component. + + Load component glyphs using GET_GLYPH, which should return whether + or not FREE_GLYPH should be called with the glyph that was loaded. + Call both functions with DCONTEXT as an argument. + + Finally, append the resulting contours, run any compound glyph + program, and return the instructed outline in *VALUE. + + Value is NULL upon success, and the type of error upon failure. */ + +TEST_STATIC const char * +sfnt_interpret_compound_glyph (struct sfnt_glyph *glyph, + struct sfnt_interpreter *interpreter, + struct sfnt_graphics_state *state, + sfnt_get_glyph_proc get_glyph, + sfnt_free_glyph_proc free_glyph, + struct sfnt_hmtx_table *hmtx, + struct sfnt_hhea_table *hhea, + struct sfnt_maxp_table *maxp, + struct sfnt_glyph_metrics *metrics, + void *dcontext, + struct sfnt_instructed_outline **value) +{ + struct sfnt_compound_glyph_context context; + const char *error; + struct sfnt_instructed_outline *outline; + size_t outline_size, temp; + + /* Set up the glyph decomposition context. */ + memset (&context, 0, sizeof context); + + /* Now start decomposing the glyph. */ + error = sfnt_interpret_compound_glyph_1 (glyph, interpreter, + state, &context, + get_glyph, free_glyph, + hmtx, hhea, maxp, + metrics, 0, 0, 0, + dcontext); + + /* If an error occurs, free the data in the context and return. */ + + if (error) + { + xfree (context.x_coordinates); + xfree (context.y_coordinates); + xfree (context.flags); + xfree (context.contour_end_points); + return error; + } + + /* Copy the compound glyph data into an instructed outline. */ + outline_size = sizeof (*outline); + + if (INT_MULTIPLY_WRAPV (context.num_end_points, + sizeof *outline->contour_end_points, + &temp) + || INT_ADD_WRAPV (outline_size, temp, &outline_size) + || INT_MULTIPLY_WRAPV (context.num_points, + sizeof *outline->x_points * 2, + &temp) + || INT_ADD_WRAPV (outline_size, temp, &outline_size) + || INT_ADD_WRAPV (context.num_points, outline_size, + &outline_size)) + { + xfree (context.x_coordinates); + xfree (context.y_coordinates); + xfree (context.flags); + xfree (context.contour_end_points); + return "Glyph exceeds maximum permissible size"; + } + + /* Allocate the outline. */ + outline = xmalloc (outline_size); + outline->num_points = context.num_points; + outline->num_contours = context.num_end_points; + outline->contour_end_points = (size_t *) (outline + 1); + outline->x_points = (sfnt_f26dot6 *) (outline->contour_end_points + + outline->num_contours); + outline->y_points = outline->x_points + outline->num_points; + outline->flags = (unsigned char *) (outline->y_points + + outline->num_points); + + /* Copy over the contour endpoints, points, and flags. */ + memcpy (outline->contour_end_points, context.contour_end_points, + outline->num_contours * sizeof *outline->contour_end_points); + memcpy (outline->x_points, context.x_coordinates, + outline->num_points * sizeof *outline->x_points); + memcpy (outline->y_points, context.y_coordinates, + outline->num_points * sizeof *outline->y_points); + memcpy (outline->flags, context.flags, context.num_points); + + /* Free the context data. */ + xfree (context.x_coordinates); + xfree (context.y_coordinates); + xfree (context.flags); + xfree (context.contour_end_points); + + *value = outline; + return NULL; +} + +#endif /* SFNT_ENABLE_HINTING */ + + + +#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)); @@ -14743,8 +15533,8 @@ main (int argc, char **argv) data[i]->format); } -#define FANCY_PPEM 12 -#define EASY_PPEM 12 +#define FANCY_PPEM 40 +#define EASY_PPEM 40 interpreter = NULL; head = sfnt_read_head_table (fd, font); @@ -15159,17 +15949,35 @@ main (int argc, char **argv) interpreter->pop_hook = sfnt_pop_hook; } - if (glyph->simple - && !sfnt_lookup_glyph_metrics (code, -1, - &metrics, - hmtx, hhea, - head, maxp)) + if (!sfnt_lookup_glyph_metrics (code, -1, + &metrics, + hmtx, hhea, + head, maxp)) { printf ("interpreting glyph\n"); interpreter->state = state; clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start); - trap = sfnt_interpret_simple_glyph (glyph, interpreter, - &metrics, &value); + if (glyph->simple) + trap + = sfnt_interpret_simple_glyph (glyph, + interpreter, + &metrics, + &value); + else +#define GG sfnt_test_get_glyph +#define FG sfnt_test_free_glyph + trap + = sfnt_interpret_compound_glyph (glyph, + interpreter, + &state, + GG, FG, + hmtx, hhea, + maxp, + &metrics, + &dcontext, + &value); +#undef GG +#undef FG clock_gettime (CLOCK_THREAD_CPUTIME_ID, &end); sub3 = timespec_sub (end, start); @@ -15197,7 +16005,8 @@ main (int argc, char **argv) } } - fprintf (stderr, "execution time: %lld sec %ld nsec\n", + fprintf (stderr, "execution time: %lld sec %ld nse" + "c\n", (long long) sub3.tv_sec, sub3.tv_nsec); } diff --git a/src/sfnt.h b/src/sfnt.h index 0845cb5be51..40c77ee123f 100644 --- a/src/sfnt.h +++ b/src/sfnt.h @@ -1135,6 +1135,11 @@ struct sfnt_interpreter_zone enum { + /* Bits 1 stands for X_SHORT_VECTOR on disk and in the tables, but + this representation is not useful in memory. Inside an + instructed glyph, this bit is repurposed to mean that the + corresponding point is a phantom point. */ + SFNT_POINT_PHANTOM = (1 << 1), /* Bits 7 and 6 of a glyph point's flags is reserved. This scaler uses it to mean that the point has been touched in one axis or another. */ @@ -1478,6 +1483,23 @@ extern const char *sfnt_interpret_simple_glyph (PROTOTYPE); #undef PROTOTYPE +#define PROTOTYPE \ + struct sfnt_glyph *, \ + struct sfnt_interpreter *, \ + struct sfnt_graphics_state *, \ + sfnt_get_glyph_proc, \ + sfnt_free_glyph_proc, \ + struct sfnt_hmtx_table *, \ + struct sfnt_hhea_table *, \ + struct sfnt_maxp_table *, \ + struct sfnt_glyph_metrics *, \ + void *, \ + struct sfnt_instructed_outline ** + +extern const char *sfnt_interpret_compound_glyph (PROTOTYPE); + +#undef PROTOTYPE + #endif /* TEST */ diff --git a/src/sfntfont.c b/src/sfntfont.c index 9ab76a3082b..cc084c79307 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -1456,6 +1456,9 @@ sfntfont_dereference_outline (struct sfnt_glyph_outline *outline) Use the offset information in the long or short loca tables LOCA_LONG and LOCA_SHORT, whichever is set. + Use the specified HMTX, HHEA and MAXP tables when instructing + compound glyphs. + If INTERPRETER is non-NULL, then possibly use the unscaled glyph metrics in METRICS and the interpreter STATE to instruct the glyph. @@ -1469,6 +1472,9 @@ sfntfont_get_glyph_outline (sfnt_glyph glyph_code, int pixel_size, int *cache_size, struct sfnt_glyf_table *glyf, struct sfnt_head_table *head, + struct sfnt_hmtx_table *hmtx, + struct sfnt_hhea_table *hhea, + struct sfnt_maxp_table *maxp, struct sfnt_loca_table_short *loca_short, struct sfnt_loca_table_long *loca_long, struct sfnt_interpreter *interpreter, @@ -1517,14 +1523,32 @@ sfntfont_get_glyph_outline (sfnt_glyph glyph_code, outline = NULL; - if (interpreter && glyph->simple) + dcontext.loca_long = loca_long; + dcontext.loca_short = loca_short; + dcontext.glyf = glyf; + + if (interpreter) { - /* Restore the interpreter state from the snapshot taken after - loading the preprogram. */ - interpreter->state = *state; + if (glyph->simple) + { + /* Restore the interpreter state from the snapshot taken + after loading the preprogram. */ + interpreter->state = *state; - error = sfnt_interpret_simple_glyph (glyph, interpreter, - metrics, &value); + error = sfnt_interpret_simple_glyph (glyph, interpreter, + metrics, &value); + } + else + /* Restoring the interpreter state is done by + sfnt_interpret_compound_glyph; all that must be done here + is to give the graphics state to that function. */ + error = sfnt_interpret_compound_glyph (glyph, interpreter, + state, + sfntfont_get_glyph, + sfntfont_free_glyph, + hmtx, hhea, maxp, + metrics, &dcontext, + &value); if (!error) { @@ -1533,10 +1557,6 @@ sfntfont_get_glyph_outline (sfnt_glyph glyph_code, } } - dcontext.loca_long = loca_long; - dcontext.loca_short = loca_short; - dcontext.glyf = glyf; - if (!outline) outline = sfnt_build_glyph_outline (glyph, head, pixel_size, sfntfont_get_glyph, @@ -2272,6 +2292,8 @@ sfntfont_measure_instructed_pcm (struct sfnt_font_info *font, sfnt_glyph glyph, font->font.pixel_size, &font->outline_cache_size, font->glyf, font->head, + font->hmtx, font->hhea, + font->maxp, font->loca_short, font->loca_long, font->interpreter, &metrics, @@ -2321,6 +2343,8 @@ sfntfont_measure_pcm (struct sfnt_font_info *font, sfnt_glyph glyph, font->font.pixel_size, &font->outline_cache_size, font->glyf, font->head, + font->hmtx, font->hhea, + font->maxp, font->loca_short, font->loca_long, NULL, NULL, NULL); @@ -2469,6 +2493,8 @@ sfntfont_draw (struct glyph_string *s, int from, int to, font->pixel_size, &info->outline_cache_size, info->glyf, info->head, + info->hmtx, info->hhea, + info->maxp, info->loca_short, info->loca_long, info->interpreter, -- 2.39.5