Imported Upstream version 2.3.1
[platform/upstream/harfbuzz.git] / src / hb-ot-layout-base-table.hh
index 33dce89..dd0fba1 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright © 2016 Elie Roux <elie.roux@telecom-bretagne.eu>
  * Copyright © 2018  Google, Inc.
+ * Copyright © 2018  Ebrahim Byagowi
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
 #ifndef HB_OT_LAYOUT_BASE_TABLE_HH
 #define HB_OT_LAYOUT_BASE_TABLE_HH
 
-#include "hb-open-type-private.hh"
-#include "hb-ot-layout-common-private.hh"
+#include "hb-open-type.hh"
+#include "hb-ot-layout-common.hh"
 
-namespace OT {
+/* To be removed */
+typedef hb_tag_t hb_ot_layout_baseline_t;
 
-#define NOT_INDEXED   ((unsigned int) -1)
+namespace OT {
 
 /*
  * BASE -- Baseline
@@ -42,30 +44,30 @@ namespace OT {
 
 struct BaseCoordFormat1
 {
-  inline int get_coord (void) const { return coordinate; }
+  hb_position_t get_coord () const { return coordinate; }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this));
+    return_trace (likely (c->check_struct (this)));
   }
 
   protected:
   HBUINT16     format;         /* Format identifier--format = 1 */
-  HBINT16      coordinate;     /* X or Y value, in design units */
+  FWORD                coordinate;     /* X or Y value, in design units */
   public:
   DEFINE_SIZE_STATIC (4);
 };
 
 struct BaseCoordFormat2
 {
-  inline int get_coord (void) const
+  hb_position_t get_coord () const
   {
     /* TODO */
     return coordinate;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
@@ -73,7 +75,7 @@ struct BaseCoordFormat2
 
   protected:
   HBUINT16     format;         /* Format identifier--format = 2 */
-  HBINT16      coordinate;     /* X or Y value, in design units */
+  FWORD                coordinate;     /* X or Y value, in design units */
   GlyphID      referenceGlyph; /* Glyph ID of control glyph */
   HBUINT16     coordPoint;     /* Index of contour point on the
                                 * reference glyph */
@@ -83,44 +85,53 @@ struct BaseCoordFormat2
 
 struct BaseCoordFormat3
 {
-  inline int get_coord (void) const
+  hb_position_t get_coord (hb_font_t *font,
+                          const VariationStore &var_store,
+                          hb_direction_t direction) const
   {
-    /* TODO */
-    return coordinate;
+    const Device &device = this+deviceTable;
+    return coordinate + (HB_DIRECTION_IS_VERTICAL (direction) ?
+                        device.get_y_delta (font, var_store) :
+                        device.get_x_delta (font, var_store));
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) && deviceTable.sanitize (c, this));
+    return_trace (likely (c->check_struct (this) &&
+                         deviceTable.sanitize (c, this)));
   }
 
   protected:
-  HBUINT16             format;         /* Format identifier--format = 3 */
-  HBINT16              coordinate;     /* X or Y value, in design units */
-  OffsetTo<Device>     deviceTable;    /* Offset to Device table for X or
-                                        * Y value, from beginning of
-                                        * BaseCoord table (may be NULL). */
+  HBUINT16     format;         /* Format identifier--format = 3 */
+  FWORD                coordinate;     /* X or Y value, in design units */
+  OffsetTo<Device>
+               deviceTable;    /* Offset to Device table for X or
+                                * Y value, from beginning of
+                                * BaseCoord table (may be NULL). */
   public:
   DEFINE_SIZE_STATIC (6);
 };
 
 struct BaseCoord
 {
-  inline int get_coord (void) const
+  hb_position_t get_coord (hb_font_t *font,
+                          const VariationStore &var_store,
+                          hb_direction_t direction) const
   {
     switch (u.format) {
     case 1: return u.format1.get_coord ();
     case 2: return u.format2.get_coord ();
-    case 3: return u.format3.get_coord ();
+    case 3: return u.format3.get_coord (font, var_store, direction);
     default:return 0;
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (!u.format.sanitize (c)) return_trace (false);
+    if (unlikely (!u.format.sanitize (c))) return_trace (false);
     switch (u.format) {
     case 1: return_trace (u.format1.sanitize (c));
     case 2: return_trace (u.format2.sanitize (c));
@@ -142,32 +153,40 @@ struct BaseCoord
 
 struct FeatMinMaxRecord
 {
-  inline int get_min_value (void) const
-  { return (this+minCoord).get_coord(); }
-
-  inline int get_max_value (void) const
-  { return (this+maxCoord).get_coord(); }
+  static int cmp (const void *key_, const void *entry_)
+  {
+    hb_tag_t key = * (hb_tag_t *) key_;
+    const FeatMinMaxRecord &entry = * (const FeatMinMaxRecord *) entry_;
+    return key < (unsigned int) entry.tag ? -1 :
+          key > (unsigned int) entry.tag ? 1 :
+          0;
+  }
 
-  inline const Tag &get_tag () const
-  { return tag; }
+  void get_min_max (const BaseCoord **min, const BaseCoord **max) const
+  {
+    if (likely (min)) *min = &(this+minCoord);
+    if (likely (max)) *max = &(this+maxCoord);
+  }
 
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-                 minCoord.sanitize (c, base) &&
-                 maxCoord.sanitize (c, base));
+    return_trace (likely (c->check_struct (this) &&
+                         minCoord.sanitize (c, this) &&
+                         maxCoord.sanitize (c, this)));
   }
 
   protected:
-  Tag                   tag;           /* 4-byte feature identification tag--must
-                                        * match feature tag in FeatureList */
-  OffsetTo<BaseCoord>   minCoord;      /* Offset to BaseCoord table that defines
-                                        * the minimum extent value, from beginning
-                                        * of MinMax table (may be NULL) */
-  OffsetTo<BaseCoord>   maxCoord;      /* Offset to BaseCoord table that defines
-                                        * the maximum extent value, from beginning
-                                        * of MinMax table (may be NULL) */
+  Tag          tag;            /* 4-byte feature identification tag--must
+                                * match feature tag in FeatureList */
+  OffsetTo<BaseCoord>
+               minCoord;       /* Offset to BaseCoord table that defines
+                                * the minimum extent value, from beginning
+                                * of MinMax table (may be NULL) */
+  OffsetTo<BaseCoord>
+               maxCoord;       /* Offset to BaseCoord table that defines
+                                * the maximum extent value, from beginning
+                                * of MinMax table (may be NULL) */
   public:
   DEFINE_SIZE_STATIC (8);
 
@@ -175,356 +194,269 @@ struct FeatMinMaxRecord
 
 struct MinMax
 {
-  inline unsigned int get_feature_tag_index (Tag featureTableTag) const
-  {
-    /* TODO bsearch */
-    unsigned int count = featMinMaxRecords.len;
-    for (unsigned int i = 0; i < count; i++)
+  void get_min_max (hb_tag_t          feature_tag,
+                          const BaseCoord **min,
+                          const BaseCoord **max) const
+  {
+    /* TODO Replace hb_bsearch() with .bsearch(). */
+    const FeatMinMaxRecord *minMaxCoord = (const FeatMinMaxRecord *)
+                                         hb_bsearch (&feature_tag, featMinMaxRecords.arrayZ,
+                                                     featMinMaxRecords.len,
+                                                     FeatMinMaxRecord::static_size,
+                                                     FeatMinMaxRecord::cmp);
+    if (minMaxCoord)
+      minMaxCoord->get_min_max (min, max);
+    else
     {
-      Tag tag = featMinMaxRecords[i].get_tag();
-      int cmp = tag.cmp(featureTableTag);
-      if (cmp == 0) return i;
-      if (cmp > 0)  return NOT_INDEXED;
+      if (likely (min)) *min = &(this+minCoord);
+      if (likely (max)) *max = &(this+maxCoord);
     }
-    return NOT_INDEXED;
-  }
-
-  inline int get_min_value (unsigned int featureTableTagIndex) const
-  {
-    if (featureTableTagIndex == NOT_INDEXED)
-      return (this+minCoord).get_coord();
-    return featMinMaxRecords[featureTableTagIndex].get_min_value();
-  }
-
-  inline int get_max_value (unsigned int featureTableTagIndex) const
-  {
-    if (featureTableTagIndex == NOT_INDEXED)
-      return (this+maxCoord).get_coord();
-    return featMinMaxRecords[featureTableTagIndex].get_max_value();
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-                 minCoord.sanitize (c, this) &&
-                 maxCoord.sanitize (c, this) &&
-                 featMinMaxRecords.sanitize (c, this));
+    return_trace (likely (c->check_struct (this) &&
+                         minCoord.sanitize (c, this) &&
+                         maxCoord.sanitize (c, this) &&
+                         featMinMaxRecords.sanitize (c, this)));
   }
 
   protected:
-  OffsetTo<BaseCoord>  minCoord;       /* Offset to BaseCoord table that defines
-                                        * minimum extent value, from the beginning
-                                        * of MinMax table (may be NULL) */
-  OffsetTo<BaseCoord>  maxCoord;       /* Offset to BaseCoord table that defines
-                                        * maximum extent value, from the beginning
-                                        * of MinMax table (may be NULL) */
-  ArrayOf<FeatMinMaxRecord>
-               featMinMaxRecords;      /* Array of FeatMinMaxRecords, in alphabetical
-                                        * order by featureTableTag */
+  OffsetTo<BaseCoord>
+               minCoord;       /* Offset to BaseCoord table that defines
+                                * minimum extent value, from the beginning
+                                * of MinMax table (may be NULL) */
+  OffsetTo<BaseCoord>
+               maxCoord;       /* Offset to BaseCoord table that defines
+                                * maximum extent value, from the beginning
+                                * of MinMax table (may be NULL) */
+  SortedArrayOf<FeatMinMaxRecord>
+               featMinMaxRecords;
+                               /* Array of FeatMinMaxRecords, in alphabetical
+                                * order by featureTableTag */
   public:
   DEFINE_SIZE_ARRAY (6, featMinMaxRecords);
 };
 
-/* TODO... */
-struct BaseLangSysRecord
-{
-  inline const Tag& get_tag(void) const
-  { return baseLangSysTag; }
-
-  inline unsigned int get_feature_tag_index (Tag featureTableTag) const
-  { return (this+minMax).get_feature_tag_index(featureTableTag); }
-
-  inline int get_min_value (unsigned int featureTableTagIndex) const
-  { return (this+minMax).get_min_value(featureTableTagIndex); }
-
-  inline int get_max_value (unsigned int featureTableTagIndex) const
-  { return (this+minMax).get_max_value(featureTableTagIndex); }
-
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-                 minMax.sanitize (c, base));
-  }
-
-  protected:
-  Tag                  baseLangSysTag;
-  OffsetTo<MinMax>     minMax;
-  public:
-  DEFINE_SIZE_STATIC (6);
-
-};
-
 struct BaseValues
 {
-  inline unsigned int get_default_base_tag_index (void) const
-  { return defaultIndex; }
-
-  inline int get_base_coord (unsigned int baselineTagIndex) const
+  const BaseCoord &get_base_coord (int baseline_tag_index) const
   {
-    return (this+baseCoords[baselineTagIndex]).get_coord();
+    if (baseline_tag_index == -1) baseline_tag_index = defaultIndex;
+    return this+baseCoords[baseline_tag_index];
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-      defaultIndex <= baseCoordCount &&
-      baseCoords.sanitize (c, this));
+    return_trace (likely (c->check_struct (this) &&
+                         baseCoords.sanitize (c, this)));
   }
 
   protected:
-  Index                                defaultIndex;
-  HBUINT16                     baseCoordCount;
-  OffsetArrayOf<BaseCoord>     baseCoords;
+  Index                defaultIndex;   /* Index number of default baseline for this
+                                * script — equals index position of baseline tag
+                                * in baselineTags array of the BaseTagList */
+  OffsetArrayOf<BaseCoord>
+               baseCoords;     /* Number of BaseCoord tables defined — should equal
+                                * baseTagCount in the BaseTagList
+                                *
+                                * Array of offsets to BaseCoord tables, from beginning of
+                                * BaseValues table — order matches baselineTags array in
+                                * the BaseTagList */
   public:
-  DEFINE_SIZE_ARRAY (6, baseCoords);
-
+  DEFINE_SIZE_ARRAY (4, baseCoords);
 };
 
-struct BaseScript {
-
-  inline unsigned int get_lang_tag_index (Tag baseLangSysTag) const
-  {
-    Tag tag;
-    int cmp;
-    for (unsigned int i = 0; i < baseLangSysCount; i++) {
-      tag = baseLangSysRecords[i].get_tag();
-      // taking advantage of alphabetical order
-      cmp = tag.cmp(baseLangSysTag);
-      if (cmp == 0) return i;
-      if (cmp > 0)  return NOT_INDEXED;
-    }
-    return NOT_INDEXED;
-  }
-
-  inline unsigned int get_feature_tag_index (unsigned int baseLangSysIndex, Tag featureTableTag) const
-  {
-    if (baseLangSysIndex == NOT_INDEXED) {
-      if (unlikely(defaultMinMax)) return NOT_INDEXED;
-      return (this+defaultMinMax).get_feature_tag_index(featureTableTag);
-    }
-    if (unlikely(baseLangSysIndex >= baseLangSysCount)) return NOT_INDEXED;
-    return baseLangSysRecords[baseLangSysIndex].get_feature_tag_index(featureTableTag);
-  }
-
-  inline int get_min_value (unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
-  {
-    if (baseLangSysIndex == NOT_INDEXED)
-      return (this+defaultMinMax).get_min_value(featureTableTagIndex);
-    return baseLangSysRecords[baseLangSysIndex].get_max_value(featureTableTagIndex);
-  }
-
-  inline int get_max_value (unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+struct BaseLangSysRecord
+{
+  static int cmp (const void *key_, const void *entry_)
   {
-    if (baseLangSysIndex == NOT_INDEXED)
-      return (this+defaultMinMax).get_min_value(featureTableTagIndex);
-    return baseLangSysRecords[baseLangSysIndex].get_max_value(featureTableTagIndex);
+    hb_tag_t key = * (hb_tag_t *) key_;
+    const BaseLangSysRecord &entry = * (const BaseLangSysRecord *) entry_;
+    return key < (unsigned int) entry.baseLangSysTag ? -1 :
+          key > (unsigned int) entry.baseLangSysTag ? 1 :
+          0;
   }
 
-  inline unsigned int get_default_base_tag_index (void) const
-  { return (this+baseValues).get_default_base_tag_index(); }
+  const MinMax &get_min_max () const
+  { return this+minMax; }
 
-  inline int get_base_coord (unsigned int baselineTagIndex) const
-  { return (this+baseValues).get_base_coord(baselineTagIndex); }
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-      baseValues.sanitize (c, this) &&
-      defaultMinMax.sanitize (c, this) &&
-      baseLangSysRecords.sanitize (c, this));
+    return_trace (likely (c->check_struct (this) &&
+                         minMax.sanitize (c, this)));
   }
 
   protected:
-  OffsetTo<BaseValues>        baseValues;
-  OffsetTo<MinMax>            defaultMinMax;
-  HBUINT16                      baseLangSysCount;
-  ArrayOf<BaseLangSysRecord>  baseLangSysRecords;
-
+  Tag          baseLangSysTag; /* 4-byte language system identification tag */
+  OffsetTo<MinMax>
+               minMax;         /* Offset to MinMax table, from beginning
+                                * of BaseScript table */
   public:
-    DEFINE_SIZE_ARRAY (8, baseLangSysRecords);
+  DEFINE_SIZE_STATIC (6);
 };
 
+struct BaseScript
+{
+  const MinMax &get_min_max (hb_tag_t language_tag) const
+  {
+    /* TODO Replace hb_bsearch() with .bsearch(). */
+    const BaseLangSysRecord* record = (const BaseLangSysRecord *)
+                                     hb_bsearch (&language_tag, baseLangSysRecords.arrayZ,
+                                                 baseLangSysRecords.len,
+                                                 BaseLangSysRecord::static_size,
+                                                 BaseLangSysRecord::cmp);
+    return record ? record->get_min_max () : this+defaultMinMax;
+  }
 
-struct BaseScriptRecord {
-
-  inline const Tag& get_tag (void) const
-  { return baseScriptTag; }
-
-  inline unsigned int get_default_base_tag_index(void) const
-  { return (this+baseScript).get_default_base_tag_index(); }
-
-  inline int get_base_coord(unsigned int baselineTagIndex) const
-  { return (this+baseScript).get_base_coord(baselineTagIndex); }
-
-  inline unsigned int get_lang_tag_index (Tag baseLangSysTag) const
-  { return (this+baseScript).get_lang_tag_index(baseLangSysTag); }
-
-  inline unsigned int get_feature_tag_index (unsigned int baseLangSysIndex, Tag featureTableTag) const
-  { return (this+baseScript).get_feature_tag_index(baseLangSysIndex, featureTableTag); }
-
-  inline int get_max_value (unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
-  { return (this+baseScript).get_max_value(baseLangSysIndex, featureTableTagIndex); }
+  const BaseCoord &get_base_coord (int baseline_tag_index) const
+  { return (this+baseValues).get_base_coord (baseline_tag_index); }
 
-  inline int get_min_value (unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
-  { return (this+baseScript).get_min_value(baseLangSysIndex, featureTableTagIndex); }
+  bool is_empty () const { return !baseValues; }
 
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-      baseScript != Null(OffsetTo<BaseScript>) &&
-      baseScript.sanitize (c, base));
+    return_trace (likely (c->check_struct (this) &&
+                         baseValues.sanitize (c, this) &&
+                         defaultMinMax.sanitize (c, this) &&
+                         baseLangSysRecords.sanitize (c, this)));
   }
 
   protected:
-  Tag                   baseScriptTag;
-  OffsetTo<BaseScript>  baseScript;
+  OffsetTo<BaseValues>
+               baseValues;     /* Offset to BaseValues table, from beginning
+                                * of BaseScript table (may be NULL) */
+  OffsetTo<MinMax>
+               defaultMinMax;  /* Offset to MinMax table, from beginning of
+                                * BaseScript table (may be NULL) */
+  SortedArrayOf<BaseLangSysRecord>
+               baseLangSysRecords;
+                               /* Number of BaseLangSysRecords
+                                * defined — may be zero (0) */
 
   public:
-    DEFINE_SIZE_STATIC (6);
+  DEFINE_SIZE_ARRAY (6, baseLangSysRecords);
 };
 
-struct BaseScriptList {
-
-  inline unsigned int get_base_script_index (Tag baseScriptTag) const
-  {
-    for (unsigned int i = 0; i < baseScriptCount; i++)
-      if (baseScriptRecords[i].get_tag() == baseScriptTag)
-        return i;
-    return NOT_INDEXED;
-  }
-
-  inline unsigned int get_default_base_tag_index (unsigned int baseScriptIndex) const
-  {
-    if (unlikely(baseScriptIndex >= baseScriptCount)) return NOT_INDEXED;
-    return baseScriptRecords[baseScriptIndex].get_default_base_tag_index();
-  }
-
-  inline int get_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
-  {
-    return baseScriptRecords[baseScriptIndex].get_base_coord(baselineTagIndex);
-  }
-
-  inline unsigned int get_lang_tag_index (unsigned int baseScriptIndex, Tag baseLangSysTag) const
-  {
-    if (unlikely(baseScriptIndex >= baseScriptCount)) return NOT_INDEXED;
-    return baseScriptRecords[baseScriptIndex].get_lang_tag_index(baseLangSysTag);
-  }
-
-  inline unsigned int get_feature_tag_index (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, Tag featureTableTag) const
-  {
-    if (unlikely(baseScriptIndex >= baseScriptCount)) return NOT_INDEXED;
-    return baseScriptRecords[baseScriptIndex].get_feature_tag_index(baseLangSysIndex, featureTableTag);
-  }
-
-  inline int get_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+struct BaseScriptList;
+struct BaseScriptRecord
+{
+  static int cmp (const void *key_, const void *entry_)
   {
-    return baseScriptRecords[baseScriptIndex].get_max_value(baseLangSysIndex, featureTableTagIndex);
+    hb_tag_t key = * (hb_tag_t *) key_;
+    const BaseScriptRecord &entry = * (const BaseScriptRecord *) entry_;
+    return key < (unsigned int) entry.baseScriptTag ? -1 :
+          key > (unsigned int) entry.baseScriptTag ? 1 :
+          0;
   }
 
-  inline int get_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
-  {
-    return baseScriptRecords[baseScriptIndex].get_min_value(baseLangSysIndex, featureTableTagIndex);
-  }
+  const BaseScript &get_base_script (const BaseScriptList *list) const
+  { return list+baseScript; }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-      baseScriptRecords.sanitize (c, this));
+    return_trace (likely (c->check_struct (this) &&
+                         baseScript.sanitize (c, base)));
   }
 
   protected:
-  HBUINT16                    baseScriptCount;
-  ArrayOf<BaseScriptRecord> baseScriptRecords;
+  Tag          baseScriptTag;  /* 4-byte script identification tag */
+  OffsetTo<BaseScript>
+               baseScript;     /* Offset to BaseScript table, from beginning
+                                * of BaseScriptList */
 
   public:
-  DEFINE_SIZE_ARRAY (4, baseScriptRecords);
-
+  DEFINE_SIZE_STATIC (6);
 };
 
-struct BaseTagList
+struct BaseScriptList
 {
+  const BaseScriptRecord *find_record (hb_tag_t script) const
+  {
+    /* TODO Replace hb_bsearch() with .bsearch(). */
+    return (const BaseScriptRecord *) hb_bsearch (&script, baseScriptRecords.arrayZ,
+                                                 baseScriptRecords.len,
+                                                 BaseScriptRecord::static_size,
+                                                 BaseScriptRecord::cmp);
+  }
 
-  inline unsigned int get_tag_index(Tag baselineTag) const
+  /* TODO: Or client should handle fallback? */
+  const BaseScript &get_base_script (hb_tag_t script) const
   {
-    for (unsigned int i = 0; i < baseTagCount; i++)
-      if (baselineTags[i] == baselineTag)
-        return i;
-    return NOT_INDEXED;
+    const BaseScriptRecord *record = find_record (script);
+    if (!record) record = find_record ((hb_script_t) HB_TAG ('D','F','L','T'));
+
+    return record ? record->get_base_script (this) : Null (BaseScript);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this));
+    return_trace (c->check_struct (this) &&
+                 baseScriptRecords.sanitize (c, this));
   }
 
   protected:
-  HBUINT16        baseTagCount;
-  SortedArrayOf<Tag>  baselineTags;
+  SortedArrayOf<BaseScriptRecord>
+                       baseScriptRecords;
 
   public:
-  DEFINE_SIZE_ARRAY (4, baselineTags);
+  DEFINE_SIZE_ARRAY (2, baseScriptRecords);
 };
 
 struct Axis
 {
-
-  inline unsigned int get_base_tag_index(Tag baselineTag) const
+  bool get_baseline (hb_ot_layout_baseline_t   baseline,
+                           hb_tag_t                  script_tag,
+                           hb_tag_t                  language_tag,
+                           const BaseCoord         **coord) const
   {
-    if (unlikely(baseTagList == Null(OffsetTo<BaseTagList>))) return NOT_INDEXED;
-    return (this+baseTagList).get_tag_index(baselineTag);
-  }
+    const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag);
+    if (base_script.is_empty ()) return false;
 
-  inline unsigned int get_default_base_tag_index_for_script_index (unsigned int baseScriptIndex) const
-  {
-    if (unlikely(baseScriptList == Null(OffsetTo<BaseScriptList>))) return NOT_INDEXED;
-    return (this+baseScriptList).get_default_base_tag_index(baseScriptIndex);
-  }
-
-  inline int get_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
-  {
-    return (this+baseScriptList).get_base_coord(baseScriptIndex, baselineTagIndex);
-  }
+    if (likely (coord)) *coord = &base_script.get_base_coord ((this+baseTagList).bsearch (baseline));
 
-  inline unsigned int get_lang_tag_index (unsigned int baseScriptIndex, Tag baseLangSysTag) const
-  {
-    if (unlikely(baseScriptList == Null(OffsetTo<BaseScriptList>))) return NOT_INDEXED;
-    return (this+baseScriptList).get_lang_tag_index(baseScriptIndex, baseLangSysTag);
+    return true;
   }
 
-  inline unsigned int get_feature_tag_index (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, Tag featureTableTag) const
+  bool get_min_max (hb_tag_t          script_tag,
+                   hb_tag_t          language_tag,
+                   hb_tag_t          feature_tag,
+                   const BaseCoord **min_coord,
+                   const BaseCoord **max_coord) const
   {
-    if (unlikely(baseScriptList == Null(OffsetTo<BaseScriptList>))) return NOT_INDEXED;
-    return (this+baseScriptList).get_feature_tag_index(baseScriptIndex, baseLangSysIndex, featureTableTag);
-  }
+    const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag);
+    if (base_script.is_empty ()) return false;
 
-  inline int get_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
-  {
-    return (this+baseScriptList).get_max_value(baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
-  }
+    base_script.get_min_max (language_tag).get_min_max (feature_tag, min_coord, max_coord);
 
-  inline int get_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
-  {
-    return (this+baseScriptList).get_min_value(baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
+    return true;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-      baseTagList.sanitize (c, this) &&
-      baseScriptList.sanitize (c, this));
+    return_trace (likely (c->check_struct (this) &&
+                         (this+baseTagList).sanitize (c) &&
+                         (this+baseScriptList).sanitize (c)));
   }
 
   protected:
-  OffsetTo<BaseTagList>     baseTagList;
-  OffsetTo<BaseScriptList>  baseScriptList;
+  OffsetTo<SortedArrayOf<Tag> >
+               baseTagList;    /* Offset to BaseTagList table, from beginning
+                                * of Axis table (may be NULL)
+                                * Array of 4-byte baseline identification tags — must
+                                * be in alphabetical order */
+  OffsetTo<BaseScriptList>
+               baseScriptList; /* Offset to BaseScriptList table, from beginning
+                                * of Axis table
+                                * Array of BaseScriptRecords, in alphabetical order
+                                * by baseScriptTag */
 
   public:
   DEFINE_SIZE_STATIC (4);
@@ -532,119 +464,72 @@ struct Axis
 
 struct BASE
 {
-  static const hb_tag_t tableTag = HB_OT_TAG_BASE;
-
-  inline bool has_vert_axis(void)
-  { return vertAxis != Null(OffsetTo<Axis>); }
+  static constexpr hb_tag_t tableTag = HB_OT_TAG_BASE;
 
-  inline bool has_horiz_axis(void)
-  { return horizAxis != Null(OffsetTo<Axis>); }
+  const Axis &get_axis (hb_direction_t direction) const
+  { return HB_DIRECTION_IS_VERTICAL (direction) ? this+vAxis : this+hAxis; }
 
-  // horizontal axis base coords:
+  const VariationStore &get_var_store () const
+  { return version.to_int () < 0x00010001u ? Null (VariationStore) : this+varStore; }
 
-  inline unsigned int get_horiz_base_tag_index(Tag baselineTag) const
+  bool get_baseline (hb_font_t               *font,
+                    hb_ot_layout_baseline_t  baseline,
+                    hb_direction_t           direction,
+                    hb_tag_t                 script_tag,
+                    hb_tag_t                 language_tag,
+                    hb_position_t           *base) const
   {
-    if (unlikely(horizAxis == Null(OffsetTo<Axis>))) return NOT_INDEXED;
-    return (this+horizAxis).get_base_tag_index(baselineTag);
-  }
+    const BaseCoord *base_coord;
+    if (!get_axis (direction).get_baseline (baseline, script_tag, language_tag, &base_coord))
+      return false;
 
-  inline unsigned int get_horiz_default_base_tag_index_for_script_index (unsigned int baseScriptIndex) const
-  {
-    if (unlikely(horizAxis == Null(OffsetTo<Axis>))) return NOT_INDEXED;
-    return (this+horizAxis).get_default_base_tag_index_for_script_index(baseScriptIndex);
+    if (likely (base && base_coord)) *base = base_coord->get_coord (font,
+                                                                   get_var_store (),
+                                                                   direction);
+    return true;
   }
 
-  inline int get_horiz_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
+  /* TODO: Expose this separately sometime? */
+  bool get_min_max (hb_font_t      *font,
+                   hb_direction_t  direction,
+                   hb_tag_t        script_tag,
+                   hb_tag_t        language_tag,
+                   hb_tag_t        feature_tag,
+                   hb_position_t  *min,
+                   hb_position_t  *max)
   {
-    return (this+horizAxis).get_base_coord(baseScriptIndex, baselineTagIndex);
-  }
-
-  // vertical axis base coords:
-
-  inline unsigned int get_vert_base_tag_index(Tag baselineTag) const
-  {
-    if (unlikely(vertAxis == Null(OffsetTo<Axis>))) return NOT_INDEXED;
-    return (this+vertAxis).get_base_tag_index(baselineTag);
-  }
-
-  inline unsigned int get_vert_default_base_tag_index_for_script_index (unsigned int baseScriptIndex) const
-  {
-    if (unlikely(vertAxis == Null(OffsetTo<Axis>))) return NOT_INDEXED;
-    return (this+vertAxis).get_default_base_tag_index_for_script_index(baseScriptIndex);
-  }
-
-  inline int get_vert_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
-  {
-    return (this+vertAxis).get_base_coord(baseScriptIndex, baselineTagIndex);
-  }
-
-  // horizontal axis min/max coords:
-
-  inline unsigned int get_horiz_lang_tag_index (unsigned int baseScriptIndex, Tag baseLangSysTag) const
-  {
-    if (unlikely(horizAxis == Null(OffsetTo<Axis>))) return NOT_INDEXED;
-    return (this+horizAxis).get_lang_tag_index (baseScriptIndex, baseLangSysTag);
-  }
-
-  inline unsigned int get_horiz_feature_tag_index (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, Tag featureTableTag) const
-  {
-    if (unlikely(horizAxis == Null(OffsetTo<Axis>))) return NOT_INDEXED;
-    return (this+horizAxis).get_feature_tag_index (baseScriptIndex, baseLangSysIndex, featureTableTag);
-  }
+    const BaseCoord *min_coord, *max_coord;
+    if (!get_axis (direction).get_min_max (script_tag, language_tag, feature_tag,
+                                          &min_coord, &max_coord))
+      return false;
 
-  inline int get_horiz_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
-  {
-    return (this+horizAxis).get_max_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
-  }
-
-  inline int get_horiz_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
-  {
-    return (this+horizAxis).get_min_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
-  }
-
-    // vertical axis min/max coords:
-
-  inline unsigned int get_vert_lang_tag_index (unsigned int baseScriptIndex, Tag baseLangSysTag) const
-  {
-    if (unlikely(vertAxis == Null(OffsetTo<Axis>))) return NOT_INDEXED;
-    return (this+vertAxis).get_lang_tag_index (baseScriptIndex, baseLangSysTag);
-  }
-
-  inline unsigned int get_vert_feature_tag_index (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, Tag featureTableTag) const
-  {
-    if (unlikely(vertAxis == Null(OffsetTo<Axis>))) return NOT_INDEXED;
-    return (this+vertAxis).get_feature_tag_index (baseScriptIndex, baseLangSysIndex, featureTableTag);
-  }
-
-  inline int get_vert_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
-  {
-    return (this+vertAxis).get_max_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
+    const VariationStore &var_store = get_var_store ();
+    if (likely (min && min_coord)) *min = min_coord->get_coord (font, var_store, direction);
+    if (likely (max && max_coord)) *max = max_coord->get_coord (font, var_store, direction);
+    return true;
   }
 
-  inline int get_vert_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
-  {
-    return (this+vertAxis).get_min_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
-  }
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-                 likely (version.major == 1) &&
-                 horizAxis.sanitize (c, this) &&
-                 vertAxis.sanitize (c, this) &&
-                 (version.to_int () < 0x00010001u || varStore.sanitize (c, this)));
+    return_trace (likely (c->check_struct (this) &&
+                         likely (version.major == 1) &&
+                         hAxis.sanitize (c, this) &&
+                         vAxis.sanitize (c, this) &&
+                         (version.to_int () < 0x00010001u || varStore.sanitize (c, this))));
   }
 
   protected:
-  FixedVersion<>  version;
-  OffsetTo<Axis>  horizAxis;
-  OffsetTo<Axis>  vertAxis;
+  FixedVersion<>version;       /* Version of the BASE table */
+  OffsetTo<Axis>hAxis;         /* Offset to horizontal Axis table, from beginning
+                                * of BASE table (may be NULL) */
+  OffsetTo<Axis>vAxis;         /* Offset to vertical Axis table, from beginning
+                                * of BASE table (may be NULL) */
   LOffsetTo<VariationStore>
-               varStore;               /* Offset to the table of Item Variation
-                                        * Store--from beginning of BASE
-                                        * header (may be NULL).  Introduced
-                                        * in version 0x00010001. */
+               varStore;       /* Offset to the table of Item Variation
+                                * Store--from beginning of BASE
+                                * header (may be NULL).  Introduced
+                                * in version 0x00010001. */
   public:
   DEFINE_SIZE_MIN (8);
 };