[var] Add hb_var_coord_t and parsing routines
diff --git a/src/hb-common.cc b/src/hb-common.cc
index 04d9fb0..25c979c 100644
--- a/src/hb-common.cc
+++ b/src/hb-common.cc
@@ -607,7 +607,8 @@
 }
 
 
-/* hb_feature_t */
+
+/* hb_feature_t and hb_var_coord_t */
 
 static bool
 parse_space (const char **pp, const char *end)
@@ -654,6 +655,28 @@
 }
 
 static bool
+parse_float (const char **pp, const char *end, float *pv)
+{
+  char buf[32];
+  unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
+  strncpy (buf, *pp, len);
+  buf[len] = '\0';
+
+  char *p = buf;
+  char *pend = p;
+  float v;
+
+  errno = 0;
+  v = strtof (p, &pend);
+  if (errno || p == pend)
+    return false;
+
+  *pv = v;
+  *pp += pend - p;
+  return true;
+}
+
+static bool
 parse_bool (const char **pp, const char *end, unsigned int *pv)
 {
   parse_space (pp, end);
@@ -673,6 +696,8 @@
   return true;
 }
 
+/* hb_feature_t */
+
 static bool
 parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
 {
@@ -687,7 +712,7 @@
 }
 
 static bool
-parse_feature_tag (const char **pp, const char *end, hb_feature_t *feature)
+parse_tag (const char **pp, const char *end, hb_tag_t *tag)
 {
   parse_space (pp, end);
 
@@ -706,7 +731,7 @@
   if (p == *pp || *pp - p > 4)
     return false;
 
-  feature->tag = hb_tag_from_string (p, *pp - p);
+  *tag = hb_tag_from_string (p, *pp - p);
 
   if (quote)
   {
@@ -759,12 +784,11 @@
   return !had_equal || had_value;
 }
 
-
 static bool
 parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
 {
   return parse_feature_value_prefix (pp, end, feature) &&
-	 parse_feature_tag (pp, end, feature) &&
+	 parse_tag (pp, end, &feature->tag) &&
 	 parse_feature_indices (pp, end, feature) &&
 	 parse_feature_value_postfix (pp, end, feature) &&
 	 parse_space (pp, end) &&
@@ -855,3 +879,63 @@
   memcpy (buf, s, len);
   buf[len] = '\0';
 }
+
+/* hb_var_coord_t */
+
+static bool
+parse_var_coord_value (const char **pp, const char *end, hb_var_coord_t *var_coord)
+{
+  parse_char (pp, end, '='); /* Optional. */
+  return parse_float (pp, end, &var_coord->value);
+}
+
+static bool
+parse_one_var_coord (const char **pp, const char *end, hb_var_coord_t *var_coord)
+{
+  return parse_tag (pp, end, &var_coord->tag) &&
+	 parse_var_coord_value (pp, end, var_coord) &&
+	 parse_space (pp, end) &&
+	 *pp == end;
+}
+
+hb_bool_t
+hb_var_coord_from_string (const char *str, int len,
+			  hb_var_coord_t *var_coord)
+{
+  hb_var_coord_t coord;
+
+  if (len < 0)
+    len = strlen (str);
+
+  if (likely (parse_one_var_coord (&str, str + len, &coord)))
+  {
+    if (var_coord)
+      *var_coord = coord;
+    return true;
+  }
+
+  if (var_coord)
+    memset (var_coord, 0, sizeof (*var_coord));
+  return false;
+}
+
+void
+hb_var_coord_to_string (hb_var_coord_t *var_coord,
+			char *buf, unsigned int size)
+{
+  if (unlikely (!size)) return;
+
+  char s[128];
+  unsigned int len = 0;
+  hb_tag_to_string (var_coord->tag, s + len);
+  len += 4;
+  while (len && s[len - 1] == ' ')
+    len--;
+  s[len++] = '=';
+  len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", var_coord->value));
+
+  assert (len < ARRAY_LENGTH (s));
+  len = MIN (len, size - 1);
+  memcpy (buf, s, len);
+  buf[len] = '\0';
+}
diff --git a/src/hb-common.h b/src/hb-common.h
index dd72a9d..e483fb8 100644
--- a/src/hb-common.h
+++ b/src/hb-common.h
@@ -362,7 +362,7 @@
 typedef void (*hb_destroy_func_t) (void *user_data);
 
 
-/* Font features. */
+/* Font features and variations. */
 
 typedef struct hb_feature_t {
   hb_tag_t      tag;
@@ -379,6 +379,19 @@
 hb_feature_to_string (hb_feature_t *feature,
 		      char *buf, unsigned int size);
 
+typedef struct hb_var_coord_t {
+  hb_tag_t tag;
+  float    value;
+} hb_var_coord_t;
+
+HB_EXTERN hb_bool_t
+hb_var_coord_from_string (const char *str, int len,
+			  hb_var_coord_t *var_coord);
+
+HB_EXTERN void
+hb_var_coord_to_string (hb_var_coord_t *var_coord,
+			char *buf, unsigned int size);
+
 
 HB_END_DECLS