[var] Implement 'avar' table mapping

Untested!
diff --git a/src/hb-ot-math.cc b/src/hb-ot-math.cc
index eb08bad..2d7e679 100644
--- a/src/hb-ot-math.cc
+++ b/src/hb-ot-math.cc
@@ -34,9 +34,7 @@
 _get_math (hb_face_t *face)
 {
   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::MATH);
-
   hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
-
   return *(layout->math.get ());
 }
 
diff --git a/src/hb-ot-var-avar-table.hh b/src/hb-ot-var-avar-table.hh
index a041934..743d587 100644
--- a/src/hb-ot-var-avar-table.hh
+++ b/src/hb-ot-var-avar-table.hh
@@ -50,7 +50,39 @@
   DEFINE_SIZE_STATIC (4);
 };
 
-typedef ArrayOf<AxisValueMap> SegmentMaps;
+struct SegmentMaps : ArrayOf<AxisValueMap>
+{
+  inline int map (int value) const
+  {
+    /* The following special-cases are not part of OpenType, which requires
+     * that at least -1, 0, and +1 must be mapped. But we include these as
+     * part of a better error recovery scheme. */
+
+    if (!len)
+      return value;
+
+    if (value <= array[0].fromCoord)
+      return value - array[0].fromCoord + array[0].toCoord;
+
+    unsigned int i;
+    unsigned int count = len;
+    for (i = 1; i < count && value > array[i].fromCoord; i++)
+      ;
+
+    if (value >= array[i].fromCoord)
+      return value - array[i].fromCoord + array[i].toCoord;
+
+    if (unlikely (array[i-1].fromCoord == array[i].fromCoord))
+      return array[i-1].toCoord;
+
+    int denom = array[i].fromCoord - array[i-1].fromCoord;
+    return array[i-1].toCoord +
+	   (array[i].toCoord - array[i-1].toCoord) *
+	   (value - array[i-1].fromCoord + denom/2) / denom;
+  }
+
+  DEFINE_SIZE_ARRAY (2, array);
+};
 
 /*
  * avar — Axis Variations Table
@@ -80,6 +112,18 @@
     return_trace (true);
   }
 
+  inline void map_coords (int *coords, unsigned int coords_length) const
+  {
+    unsigned int count = MIN<unsigned int> (coords_length, axisCount);
+
+    const SegmentMaps *map = &axisSegmentMapsZ;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      coords[i] = map->map (coords[i]);
+      map = &StructAfter<SegmentMaps> (*map);
+    }
+  }
+
   protected:
   FixedVersion<>version;	/* Version of the avar table
 				 * initially set to 0x00010000u */
diff --git a/src/hb-ot-var.cc b/src/hb-ot-var.cc
index 76016e6..d4d16df 100644
--- a/src/hb-ot-var.cc
+++ b/src/hb-ot-var.cc
@@ -33,19 +33,24 @@
 
 HB_SHAPER_DATA_ENSURE_DECLARE(ot, face)
 
+/*
+ * fvar/avar
+ */
+
 static inline const OT::fvar&
 _get_fvar (hb_face_t *face)
 {
   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::fvar);
-
   hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
-
   return *(layout->fvar.get ());
 }
-
-/*
- * fvar/avar
- */
+static inline const OT::avar&
+_get_avar (hb_face_t *face)
+{
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::avar);
+  hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
+  return *(layout->avar.get ());
+}
 
 /**
  * hb_ot_var_has_data:
@@ -131,7 +136,8 @@
       coords[axis_index] = fvar.normalize_axis_value (axis_index, variations[i].value);
   }
 
-  /* TODO avar */
+  const OT::avar &avar = _get_avar (face);
+  avar.map_coords (coords, coords_length);
 }
 
 /**
@@ -149,5 +155,6 @@
   for (unsigned int i = 0; i < coords_length; i++)
     normalized_coords[i] = fvar.normalize_axis_value (i, design_coords[i]);
 
-  /* TODO avar */
+  const OT::avar &avar = _get_avar (face);
+  avar.map_coords (normalized_coords, coords_length);
 }