[style] New experimental API, hb_style_get_value

Searches variation axes of a hb_font_t object for a specific axis first,
if not set, then tries to get default style values from different
tables of the font.
diff --git a/src/Makefile.sources b/src/Makefile.sources
index 81b5cde..d8fe508 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -159,6 +159,7 @@
 	hb-shaper.hh \
 	hb-static.cc \
 	hb-string-array.hh \
+	hb-style.cc \
 	hb-ucd-table.hh \
 	hb-ucd.cc \
 	hb-unicode-emoji-table.hh \
@@ -213,6 +214,7 @@
 	hb-set.h \
 	hb-shape-plan.h \
 	hb-shape.h \
+	hb-style.h \
 	hb-unicode.h \
 	hb-version.h \
 	hb.h \
diff --git a/src/harfbuzz.cc b/src/harfbuzz.cc
index 6a07a9e..226d013 100644
--- a/src/harfbuzz.cc
+++ b/src/harfbuzz.cc
@@ -43,6 +43,7 @@
 #include "hb-shape.cc"
 #include "hb-shaper.cc"
 #include "hb-static.cc"
+#include "hb-style.cc"
 #include "hb-ucd.cc"
 #include "hb-unicode.cc"
 #include "hb-glib.cc"
diff --git a/src/hb-config.hh b/src/hb-config.hh
index 07659fa..fc8d424 100644
--- a/src/hb-config.hh
+++ b/src/hb-config.hh
@@ -76,7 +76,7 @@
 #define HB_NO_SETLOCALE
 #define HB_NO_OT_FONT_GLYPH_NAMES
 #define HB_NO_OT_SHAPE_FRACTIONS
-#define HB_NO_STAT
+#define HB_NO_STYLE
 #define HB_NO_SUBSET_LAYOUT
 #define HB_NO_VAR
 #endif
diff --git a/src/hb-ot-face-table-list.hh b/src/hb-ot-face-table-list.hh
index 6fa9baf..5519d4f 100644
--- a/src/hb-ot-face-table-list.hh
+++ b/src/hb-ot-face-table-list.hh
@@ -53,13 +53,14 @@
 HB_OT_TABLE (OT, hhea)
 HB_OT_ACCELERATOR (OT, hmtx)
 HB_OT_TABLE (OT, OS2)
-#if !defined(HB_NO_OT_FONT_GLYPH_NAMES) || !defined(HB_NO_METRICS)
+#if !defined(HB_NO_OT_FONT_GLYPH_NAMES) || !defined(HB_NO_METRICS) || !defined(HB_NO_STYLE)
 HB_OT_ACCELERATOR (OT, post)
 #endif
 #ifndef HB_NO_NAME
 HB_OT_ACCELERATOR (OT, name)
 #endif
-#ifndef HB_NO_STAT
+#ifndef HB_NO_STYLE
+HB_OT_TABLE (AAT, fdsc)
 HB_OT_TABLE (OT, STAT)
 #endif
 #ifndef HB_NO_META
diff --git a/src/hb-ot-stat-table.hh b/src/hb-ot-stat-table.hh
index 8813523..655064b 100644
--- a/src/hb-ot-stat-table.hh
+++ b/src/hb-ot-stat-table.hh
@@ -59,6 +59,9 @@
 
 struct AxisValueFormat1
 {
+  unsigned int get_axis_index () const { return axisIndex; }
+  float get_value ()             const { return value.to_float (); }
+
   hb_ot_name_id_t get_value_name_id () const { return valueNameID; }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -84,6 +87,9 @@
 
 struct AxisValueFormat2
 {
+  unsigned int get_axis_index () const { return axisIndex; }
+  float get_value ()             const { return nominalValue.to_float (); }
+
   hb_ot_name_id_t get_value_name_id () const { return valueNameID; }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -113,6 +119,9 @@
 
 struct AxisValueFormat3
 {
+  unsigned int get_axis_index () const { return axisIndex; }
+  float get_value ()             const { return value.to_float (); }
+
   hb_ot_name_id_t get_value_name_id () const { return valueNameID; }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -140,6 +149,9 @@
 
 struct AxisValueRecord
 {
+  unsigned int get_axis_index () const { return axisIndex; }
+  float get_value ()             const { return value.to_float (); }
+
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -157,6 +169,9 @@
 
 struct AxisValueFormat4
 {
+  const AxisValueRecord &get_axis_record (unsigned int axis_index) const
+  { return axisValues.as_array (axisCount)[axis_index]; }
+
   hb_ot_name_id_t get_value_name_id () const { return valueNameID; }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -183,6 +198,30 @@
 
 struct AxisValue
 {
+  bool get_value (unsigned int axis_index) const
+  {
+    switch (u.format)
+    {
+    case 1: return u.format1.get_value ();
+    case 2: return u.format2.get_value ();
+    case 3: return u.format3.get_value ();
+    case 4: return u.format4.get_axis_record (axis_index).get_value ();
+    default:return 0;
+    }
+  }
+
+  unsigned int get_axis_index () const
+  {
+    switch (u.format)
+    {
+    case 1: return u.format1.get_axis_index ();
+    case 2: return u.format2.get_axis_index ();
+    case 3: return u.format3.get_axis_index ();
+    /* case 4: Makes more sense for variable fonts which are handled by fvar in hb-style */
+    default:return -1;
+    }
+  }
+
   hb_ot_name_id_t get_value_name_id () const
   {
     switch (u.format)
@@ -226,6 +265,8 @@
 
 struct StatAxisRecord
 {
+  int cmp (hb_tag_t key) const { return tag.cmp (key); }
+
   hb_ot_name_id_t get_name_id () const { return nameID; }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -251,6 +292,38 @@
 
   bool has_data () const { return version.to_int (); }
 
+  bool find_axis_index (hb_tag_t tag, unsigned int *axis_index) const
+  {
+    hb_array_t<const StatAxisRecord> axes = get_design_axes ();
+    /* TODO: add lfind in hb_array_t and use it in here and fvar's find_axis_info */
+    for (unsigned int i = 0; i < axes.length; i++)
+      if (!axes[i].cmp (tag))
+      {
+	*axis_index = i;
+	return true;
+      }
+    return false;
+  }
+
+  bool get_value (hb_tag_t tag, float *value) const
+  {
+    unsigned int axis_index;
+    if (!find_axis_index (tag, &axis_index)) return false;
+
+    hb_array_t<const OffsetTo<AxisValue>> axis_values = get_axis_value_offsets ();
+    for (unsigned int i = 0; i < axis_values.length; i++)
+    {
+      const AxisValue& axis_value = (this + axis_values[i]);
+      if (axis_value.get_axis_index () == axis_index)
+      {
+	if (value)
+	  *value = axis_value.get_value (axis_index);
+	return true;
+      }
+    }
+    return false;
+  }
+
   unsigned get_design_axis_count () const { return designAxisCount; }
 
   hb_ot_name_id_t get_axis_record_name_id (unsigned axis_record_index) const
diff --git a/src/hb-style.cc b/src/hb-style.cc
new file mode 100644
index 0000000..dd54fdd
--- /dev/null
+++ b/src/hb-style.cc
@@ -0,0 +1,110 @@
+/*
+ * Copyright © 2019  Ebrahim Byagowi
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#include "hb.hh"
+
+#ifndef HB_NO_STYLE
+
+#include "hb-aat-fdsc-table.hh"
+#include "hb-ot-var-avar-table.hh"
+#include "hb-ot-var-fvar-table.hh"
+#include "hb-ot-stat-table.hh"
+#include "hb-ot-os2-table.hh"
+#include "hb-ot-head-table.hh"
+#include "hb-ot-post-table.hh"
+#include "hb-ot-face.hh"
+
+/**
+ * hb_style_get_value:
+ * @font: a #hb_font_t object.
+ * @style_tag: a style tag.
+ *
+ * Searches variation axes of a hb_font_t object for a specific axis first,
+ * if not set, then tries to get default style values from different
+ * tables of the font.
+ *
+ * Returns: Corresponding axis or default value to a style tag.
+ *
+ * Since: REPLACEME
+ **/
+float
+hb_style_get_value (hb_font_t *font, hb_style_tag_t style_tag)
+{
+  hb_face_t *face = font->face;
+
+#ifndef HB_NO_VAR
+  hb_ot_var_axis_info_t axis;
+  if (hb_ot_var_find_axis_info (face, style_tag, &axis))
+  {
+    if (axis.axis_index < font->num_coords) return font->design_coords[axis.axis_index];
+    /* If a face is variable, fvar's default_value is better than STAT records */
+    return axis.default_value;
+  }
+#endif
+
+  /* STAT */
+  float value;
+  if (face->table.STAT->get_value (style_tag, &value))
+    return value;
+
+  /* Check Apple's fdsc as OS2 table is optional in AAT */
+  const AAT::FontDescriptor &descriptor = face->table.fdsc->get_descriptor (style_tag);
+  if (descriptor.has_data ())
+  {
+    float value = descriptor.get_value ();
+    /* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6fdsc.html */
+    /* Percent weight relative to regular weight. */
+    if (style_tag == HB_STYLE_TAG_WEIGHT) value *= 400.f;
+    /* Percent width relative to regular width. */
+    if (style_tag == HB_STYLE_TAG_WIDTH) value *= 100.f;
+    return value;
+  }
+
+  switch ((unsigned) style_tag)
+  {
+  case HB_STYLE_TAG_ITALIC:
+    return face->table.OS2->is_italic () || face->table.head->is_italic () ? 1 : 0;
+  case HB_STYLE_TAG_OPTICAL_SIZE:
+  {
+    unsigned int lower, upper;
+    return face->table.OS2->v5 ().get_optical_size (&lower, &upper)
+	   ? (float) (lower + upper) / 2.f
+	   : 12.f;
+  }
+  case HB_STYLE_TAG_SLANT:
+    return face->table.post->table->italicAngle.to_float ();
+  case HB_STYLE_TAG_WIDTH:
+    return face->table.OS2->has_data ()
+	   ? face->table.OS2->get_width ()
+	   : (face->table.head->is_condensed () ? 75 : 100);
+  case HB_STYLE_TAG_WEIGHT:
+    return face->table.OS2->has_data ()
+	   ? face->table.OS2->usWeightClass
+	   : (face->table.head->is_bold () ? 700 : 400);
+  default:
+    return 0;
+  }
+}
+
+#endif
diff --git a/src/hb-style.h b/src/hb-style.h
new file mode 100644
index 0000000..ee84b72
--- /dev/null
+++ b/src/hb-style.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright © 2019  Ebrahim Byagowi
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HB_H_IN
+#error "Include <hb.h> instead."
+#endif
+
+#ifndef HB_STYLE_H
+#define HB_STYLE_H
+
+#include "hb.h"
+
+HB_BEGIN_DECLS
+
+
+/**
+ * hb_style_tag_t:
+ * @HB_STYLE_TAG_ITALIC: Used to vary between non-italic and italic.
+ * A value of 0 can be interpreted as "Roman" (non-italic); a value of 1 can
+ * be interpreted as (fully) italic.
+ * @HB_STYLE_TAG_OPTICAL_SIZE: Used to vary design to suit different text sizes.
+ * Non-zero. Values can be interpreted as text size, in points.
+ * @HB_STYLE_TAG_SLANT: Used to vary between upright and slanted text. Values
+ * must be greater than -90 and less than +90. Values can be interpreted as
+ * the angle, in counter-clockwise degrees, of oblique slant from whatever the
+ * designer considers to be upright for that font design.
+ * @HB_STYLE_TAG_WIDTH: Used to vary width of text from narrower to wider.
+ * Non-zero. Values can be interpreted as a percentage of whatever the font
+ * designer considers “normal width” for that font design.
+ * @HB_STYLE_TAG_WEIGHT: Used to vary stroke thicknesses or other design details
+ * to give variation from lighter to blacker. Values can be interpreted in direct
+ * comparison to values for usWeightClass in the OS/2 table,
+ * or the CSS font-weight property.
+ *
+ * Defined by https://docs.microsoft.com/en-us/typography/opentype/spec/dvaraxisreg
+ *
+ * Since: REPLACEME
+ **/
+typedef enum {
+  HB_STYLE_TAG_ITALIC		= HB_TAG ('i','t','a','l'),
+  HB_STYLE_TAG_OPTICAL_SIZE	= HB_TAG ('o','p','s','z'),
+  HB_STYLE_TAG_SLANT		= HB_TAG ('s','l','n','t'),
+  HB_STYLE_TAG_WIDTH		= HB_TAG ('w','d','t','h'),
+  HB_STYLE_TAG_WEIGHT		= HB_TAG ('w','g','h','t'),
+
+  _HB_STYLE_TAG_MAX_VALUE	= HB_TAG_MAX_SIGNED /*< skip >*/
+} hb_style_tag_t;
+
+HB_EXTERN float
+hb_style_get_value (hb_font_t *font, hb_style_tag_t style_tag);
+
+HB_END_DECLS
+
+#endif /* HB_STYLE_H */
diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc
index 232131c..9a5d74b 100644
--- a/src/hb-subset-plan.cc
+++ b/src/hb-subset-plan.cc
@@ -301,7 +301,7 @@
 _nameid_closure (hb_face_t *face,
 		 hb_set_t  *nameids)
 {
-#ifndef HB_NO_STAT
+#ifndef HB_NO_STYLE
   face->table.STAT->collect_name_ids (nameids);
 #endif
 #ifndef HB_NO_VAR
diff --git a/src/hb.h b/src/hb.h
index b0cc06c..360686c 100644
--- a/src/hb.h
+++ b/src/hb.h
@@ -39,6 +39,7 @@
 #include "hb-set.h"
 #include "hb-shape.h"
 #include "hb-shape-plan.h"
+#include "hb-style.h"
 #include "hb-unicode.h"
 #include "hb-version.h"