emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

feature/android b78ef9bcd1b 1/2: Update Android port


From: Po Lu
Subject: feature/android b78ef9bcd1b 1/2: Update Android port
Date: Thu, 23 Mar 2023 22:50:53 -0400 (EDT)

branch: feature/android
commit b78ef9bcd1bf34bec877b4fd30f2bbb97cb0919c
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>

    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



reply via email to

[Prev in Thread] Current Thread [Next in Thread]