MATH Table: Add API to access glyph info.
authorFrédéric Wang <fred.wang@free.fr>
Thu, 25 Aug 2016 09:15:31 +0000 (11:15 +0200)
committerBehdad Esfahbod <behdad@behdad.org>
Mon, 26 Sep 2016 10:32:25 +0000 (11:32 +0100)
src/hb-ot-layout-math-table.hh
src/hb-ot-layout.cc
src/hb-ot-layout.h
src/hb-ot-math.h
test/api/fonts/MathTestFontPartial1.otf [new file with mode: 0644]
test/api/fonts/MathTestFontPartial2.otf [new file with mode: 0644]
test/api/fonts/MathTestFontPartial3.otf [new file with mode: 0644]
test/api/test-ot-layout-math.c

index e65680b5aca7ca0cfc720e344c9cc837aa15b1ce..2eb0d92283fe566630ea67c8f3089dcbcc96af99 100644 (file)
@@ -159,6 +159,267 @@ public:
   DEFINE_SIZE_STATIC (214);
 };
 
+struct MathItalicsCorrectionInfo
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+                  coverage.sanitize (c, this) &&
+                  italicsCorrection.sanitize (c, this));
+  }
+
+  inline bool get_value (hb_font_t *font, hb_codepoint_t glyph,
+                         hb_position_t &value) const
+  {
+    unsigned int index = (this+coverage).get_coverage (glyph);
+    if (likely (index == NOT_COVERED)) return false;
+    if (unlikely (index >= italicsCorrection.len)) return false;
+    value = italicsCorrection[index].get_x_value(font, this);
+    return true;
+  }
+
+protected:
+  OffsetTo<Coverage>       coverage;          /* Offset to Coverage table -
+                                                 from the beginning of
+                                                 MathItalicsCorrectionInfo
+                                                 table. */
+  ArrayOf<MathValueRecord> italicsCorrection; /* Array of MathValueRecords
+                                                 defining italics correction
+                                                 values for each
+                                                 covered glyph. */
+
+public:
+  DEFINE_SIZE_ARRAY (2 + 2, italicsCorrection);
+};
+
+struct MathTopAccentAttachment
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+                  topAccentCoverage.sanitize (c, this) &&
+                  topAccentAttachment.sanitize (c, this));
+  }
+
+  inline bool get_value (hb_font_t *font, hb_codepoint_t glyph,
+                         hb_position_t &value) const
+  {
+    unsigned int index = (this+topAccentCoverage).get_coverage (glyph);
+    if (likely (index == NOT_COVERED)) return false;
+    if (unlikely (index >= topAccentAttachment.len)) return false;
+    value = topAccentAttachment[index].get_x_value(font, this);
+    return true;
+  }
+
+protected:
+  OffsetTo<Coverage>       topAccentCoverage;   /* Offset to Coverage table -
+                                                   from the beginning of
+                                                   MathTopAccentAttachment
+                                                   table. */
+  ArrayOf<MathValueRecord> topAccentAttachment; /* Array of MathValueRecords
+                                                   defining top accent
+                                                   attachment points for each
+                                                   covered glyph. */
+
+public:
+  DEFINE_SIZE_ARRAY (2 + 2, topAccentAttachment);
+};
+
+struct MathKern
+{
+  inline bool sanitize_math_value_records (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    unsigned int count = 2 * heightCount + 1;
+    for (unsigned int i = 0; i < count; i++)
+      if (!mathValueRecords[i].sanitize (c, this)) return_trace (false);
+    return_trace (true);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+                  c->check_array (mathValueRecords,
+                                  mathValueRecords[0].static_size,
+                                  2 * heightCount + 1) &&
+                  sanitize_math_value_records (c));
+  }
+
+  inline hb_position_t get_value (hb_font_t *font,
+                                  hb_position_t &correction_height) const
+  {
+    const MathValueRecord* correctionHeight = mathValueRecords;
+    const MathValueRecord* kernValue = mathValueRecords + heightCount;
+    // The description of the MathKern table is a ambiguous, but interpreting
+    // "between the two heights found at those indexes" for 0 < i < len as
+    //
+    //   correctionHeight[i-1] < correction_height <= correctionHeight[i]
+    //
+    // makes the result consistent with the limit cases and we can just use the
+    // binary search algorithm of std::upper_bound:
+    unsigned int count = heightCount;
+    unsigned int i = 0;
+    while (count > 0) {
+      unsigned int half = count / 2;
+      hb_position_t height =
+        correctionHeight[i + half].get_y_value(font, this);
+      if (height < correction_height) {
+        i += half + 1;
+        count -= half + 1;
+      } else
+        count = half;
+    }
+    return kernValue[i].get_x_value(font, this);
+  }
+
+protected:
+  USHORT          heightCount;
+  MathValueRecord mathValueRecords[VAR]; /* Array of correction heights at
+                                            which the kern value changes.
+                                            Sorted by the height value in
+                                            design units. */
+                                         /* Array of kern values corresponding
+                                            to heights. */
+
+public:
+  DEFINE_SIZE_ARRAY (2, mathValueRecords);
+};
+
+struct MathKernInfoRecord
+{
+  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+                  mathKern[HB_OT_MATH_KERN_TOP_RIGHT].sanitize (c, base) &&
+                  mathKern[HB_OT_MATH_KERN_TOP_LEFT].sanitize (c, base) &&
+                  mathKern[HB_OT_MATH_KERN_BOTTOM_RIGHT].sanitize (c, base) &&
+                  mathKern[HB_OT_MATH_KERN_BOTTOM_LEFT].sanitize (c, base));
+  }
+
+  inline bool has_math_kern (hb_ot_math_kern_t kern) const {
+    return mathKern[kern] != 0;
+  }
+  inline const MathKern &get_math_kern (hb_ot_math_kern_t kern,
+                                        const void *base) const {
+    return base+mathKern[kern];
+  }
+
+protected:
+  /* Offset to MathKern table for each corner -
+     from the beginning of MathKernInfo table. May be NULL. */
+  OffsetTo<MathKern> mathKern[HB_OT_MATH_KERN_BOTTOM_LEFT -
+                              HB_OT_MATH_KERN_TOP_RIGHT + 1];
+
+public:
+  DEFINE_SIZE_STATIC (2 * (HB_OT_MATH_KERN_BOTTOM_LEFT -
+                           HB_OT_MATH_KERN_TOP_RIGHT + 1));
+};
+
+struct MathKernInfo
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+                  mathKernCoverage.sanitize (c, this) &&
+                  mathKernInfoRecords.sanitize (c, this));
+  }
+
+  inline bool
+  get_math_kern_info_record (hb_codepoint_t glyph,
+                             const MathKernInfoRecord *&record) const
+  {
+    unsigned int index = (this+mathKernCoverage).get_coverage (glyph);
+    if (likely (index == NOT_COVERED)) return false;
+    if (unlikely (index >= mathKernInfoRecords.len)) return false;
+    record = &mathKernInfoRecords[index];
+    return true;
+  }
+
+protected:
+  OffsetTo<Coverage>          mathKernCoverage;    /* Offset to Coverage table -
+                                                      from the beginning of the
+                                                      MathKernInfo table. */
+  ArrayOf<MathKernInfoRecord> mathKernInfoRecords; /* Array of
+                                                      MathKernInfoRecords,
+                                                      per-glyph information for
+                                                      mathematical positioning
+                                                      of subscripts and
+                                                      superscripts. */
+
+public:
+  DEFINE_SIZE_ARRAY (2 + 2, mathKernInfoRecords);
+};
+
+struct MathGlyphInfo
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+                  mathItalicsCorrectionInfo.sanitize (c, this) &&
+                  mathTopAccentAttachment.sanitize (c, this) &&
+                  extendedShapeCoverage.sanitize (c, this) &&
+                  mathKernInfo.sanitize(c, this));
+  }
+
+  inline bool has_math_italics_correction_info (void) const {
+    return mathItalicsCorrectionInfo != 0;
+  }
+  inline const MathItalicsCorrectionInfo&
+  get_math_italics_correction_info (void) const {
+    return this+mathItalicsCorrectionInfo;
+  }
+
+  inline bool has_math_top_accent_attachment (void) const {
+    return mathTopAccentAttachment != 0;
+  }
+  inline const MathTopAccentAttachment&
+  get_math_top_accent_attachment (void) const {
+    return this+mathTopAccentAttachment;
+  }
+
+  inline bool is_extended_shape (hb_codepoint_t glyph) const
+  {
+    if (likely (extendedShapeCoverage == 0)) return false;
+    unsigned int index = (this+extendedShapeCoverage).get_coverage (glyph);
+    if (likely (index == NOT_COVERED)) return false;
+    return true;
+  }
+
+  inline bool has_math_kern_info (void) const { return mathKernInfo != 0; }
+  inline const MathKernInfo &get_math_kern_info (void) const {
+    return this+mathKernInfo;
+  }
+
+protected:
+  /* Offset to MathItalicsCorrectionInfo table -
+     from the beginning of MathGlyphInfo table. */
+  OffsetTo<MathItalicsCorrectionInfo> mathItalicsCorrectionInfo;
+
+  /* Offset to MathTopAccentAttachment table -
+     from the beginning of MathGlyphInfo table. */
+  OffsetTo<MathTopAccentAttachment> mathTopAccentAttachment;
+
+  /* Offset to coverage table for Extended Shape glyphs -
+     from the beginning of MathGlyphInfo table. When the left or right glyph of
+     a box is an extended shape variant, the (ink) box (and not the default
+     position defined by values in MathConstants table) should be used for
+     vertical positioning purposes. May be NULL.. */
+  OffsetTo<Coverage> extendedShapeCoverage;
+
+   /* Offset to MathKernInfo table -
+      from the beginning of MathGlyphInfo table. */
+  OffsetTo<MathKernInfo> mathKernInfo;
+
+public:
+  DEFINE_SIZE_STATIC (4 * 2);
+};
+
 /*
  * MATH -- The MATH Table
  */
@@ -172,7 +433,8 @@ struct MATH
     TRACE_SANITIZE (this);
     return_trace (version.sanitize (c) &&
                   likely (version.major == 1) &&
-                  mathConstants.sanitize (c, this));
+                  mathConstants.sanitize (c, this) &&
+                  mathGlyphInfo.sanitize (c, this));
   }
 
   inline bool has_math_constants (void) const { return mathConstants != 0; }
@@ -180,13 +442,18 @@ struct MATH
     return this+mathConstants;
   }
 
+  inline bool has_math_glyph_info (void) const { return mathGlyphInfo != 0; }
+  inline const MathGlyphInfo &get_math_glyph_info (void) const {
+    return this+mathGlyphInfo;
+  }
 protected:
   FixedVersion<>version;                 /* Version of the MATH table
                                           * initially set to 0x00010000u */
   OffsetTo<MathConstants> mathConstants; /* MathConstants table */
+  OffsetTo<MathGlyphInfo> mathGlyphInfo; /* MathGlyphInfo table */
 
 public:
-  DEFINE_SIZE_STATIC (6);
+  DEFINE_SIZE_STATIC (8);
 };
 
 } /* mathspace OT */
index 2ae138876e8af55be87a932826e7b119bf27ef91..f14611b47d9e5a7b13531d4f65f18a40e5475e81 100644 (file)
@@ -1269,3 +1269,117 @@ hb_ot_layout_get_math_constant (hb_font_t *font,
   return math.has_math_constants() ?
     math.get_math_constants().get_value(font, constant) : 0;
 }
+
+/**
+ * hb_ot_layout_get_math_italic_correction:
+ *
+ * @font: #hb_font_t from which to retrieve the value
+ * @glyph: glyph index from which to retrieve the value
+ *
+ * Return value: the italic correction of the glyph or 0
+ *
+ * Since: ????
+ **/
+HB_EXTERN hb_position_t
+hb_ot_layout_get_math_italic_correction (hb_font_t *font,
+                                         hb_codepoint_t glyph)
+{
+  const OT::MATH &math = _get_math (font->face);
+  if (math.has_math_glyph_info()) {
+    const OT::MathGlyphInfo &glyphInfo = math.get_math_glyph_info();
+    if (glyphInfo.has_math_italics_correction_info()) {
+      hb_position_t value;
+      if (glyphInfo.get_math_italics_correction_info().get_value(font, glyph,
+                                                                 value))
+        return value;
+    }
+  }
+  return 0;
+}
+
+/**
+ * hb_ot_layout_get_math_top_accent_attachment:
+ *
+ * @font: #hb_font_t from which to retrieve the value
+ * @glyph: glyph index from which to retrieve the value
+ *
+ * Return value: the top accent attachment of the glyph or 0
+ *
+ * Since: ????
+ **/
+HB_EXTERN hb_position_t
+hb_ot_layout_get_math_top_accent_attachment (hb_font_t *font,
+                                             hb_codepoint_t glyph)
+{
+  const OT::MATH &math = _get_math (font->face);
+  if (math.has_math_glyph_info()) {
+    const OT::MathGlyphInfo &glyphInfo = math.get_math_glyph_info();
+    if (glyphInfo.has_math_top_accent_attachment()) {
+      hb_position_t value;
+      if (glyphInfo.get_math_top_accent_attachment().get_value(font, glyph,
+                                                               value))
+        return value;
+    }
+  }
+  return 0;
+}
+
+/**
+ * hb_ot_layout_is_math_extended_shape:
+ *
+ * @font: a #hb_font_t to test
+ * @glyph: a glyph index to test
+ *
+ * Return value: #TRUE if the glyph is an extended shape and #FALSE otherwise
+ *
+ * Since: ????
+ **/
+HB_EXTERN hb_bool_t
+hb_ot_layout_is_math_extended_shape (hb_face_t *face,
+                                     hb_codepoint_t glyph)
+{
+  const OT::MATH &math = _get_math (face);
+  return math.has_math_glyph_info() &&
+    math.get_math_glyph_info().is_extended_shape(glyph);
+}
+
+/**
+ * hb_ot_layout_get_math_kerning:
+ *
+ * @font: #hb_font_t from which to retrieve the value
+ * @glyph: glyph index from which to retrieve the value
+ * @kern: the #hb_ot_math_kern_t from which to retrieve the value
+ * @correction_height: the correction height to use to determine the kerning.
+ *
+ * This function tries to retrieve the MathKern table for the specified font,
+ * glyph and #hb_ot_math_kern_t. Then it browses the list of heights from the
+ * MathKern table to find one value that is greater or equal to specified
+ * correction_height. If one is found the corresponding value from the list of
+ * kerns is returned and otherwise the last kern value is returned.
+ *
+ * Return value: requested kerning or 0
+ *
+ * Since: ????
+ **/
+HB_EXTERN hb_position_t
+hb_ot_layout_get_math_kerning (hb_font_t *font,
+                               hb_codepoint_t glyph,
+                               hb_ot_math_kern_t kern,
+                               hb_position_t correction_height)
+{
+  const OT::MATH &math = _get_math (font->face);
+  if (math.has_math_glyph_info()) {
+    const OT::MathGlyphInfo &glyphInfo = math.get_math_glyph_info();
+    if (glyphInfo.has_math_kern_info()) {
+      const OT::MathKernInfo &kernInfo = glyphInfo.get_math_kern_info();
+      const OT::MathKernInfoRecord *kernInfoRecord;
+      if (kernInfo.get_math_kern_info_record(glyph, kernInfoRecord) &&
+          kernInfoRecord->has_math_kern(kern)) {
+        return kernInfoRecord->
+          get_math_kern(kern, &kernInfo).get_value(font, correction_height);
+      }
+    }
+  }
+
+  return 0;
+}
index 501082e281a9b1d487f44be8e311954e9f3701f6..ffb6321c7f1d1b8410edd4083b6be78e2730a951 100644 (file)
@@ -310,6 +310,24 @@ HB_EXTERN hb_position_t
 hb_ot_layout_get_math_constant (hb_font_t *font,
                                 hb_ot_math_constant_t constant);
 
+HB_EXTERN hb_position_t
+hb_ot_layout_get_math_italic_correction (hb_font_t *font,
+                                         hb_codepoint_t glyph);
+
+HB_EXTERN hb_position_t
+hb_ot_layout_get_math_top_accent_attachment (hb_font_t *font,
+                                             hb_codepoint_t glyph);
+
+HB_EXTERN hb_bool_t
+hb_ot_layout_is_math_extended_shape (hb_face_t *face,
+                                     hb_codepoint_t glyph);
+
+HB_EXTERN hb_position_t
+hb_ot_layout_get_math_kerning (hb_font_t *font,
+                               hb_codepoint_t glyph,
+                               hb_ot_math_kern_t kern,
+                               hb_position_t correction_height);
+
 
 HB_END_DECLS
 
index 7f0ec2713a43310f82325e019c34b126c2fba0c2..a62b4b612c8acd09b63b8277afd706c4f20b10e6 100644 (file)
@@ -95,6 +95,13 @@ typedef enum {
   HB_OT_MATH_CONSTANT_RADICAL_DEGREE_BOTTOM_RAISE_PERCENT = 55
 } hb_ot_math_constant_t;
 
+typedef enum {
+  HB_OT_MATH_KERN_TOP_RIGHT = 0,
+  HB_OT_MATH_KERN_TOP_LEFT = 1,
+  HB_OT_MATH_KERN_BOTTOM_RIGHT = 2,
+  HB_OT_MATH_KERN_BOTTOM_LEFT = 3
+} hb_ot_math_kern_t;
+
 HB_END_DECLS
 
 #endif /* HB_OT_MATH_H */
diff --git a/test/api/fonts/MathTestFontPartial1.otf b/test/api/fonts/MathTestFontPartial1.otf
new file mode 100644 (file)
index 0000000..b3bf36e
Binary files /dev/null and b/test/api/fonts/MathTestFontPartial1.otf differ
diff --git a/test/api/fonts/MathTestFontPartial2.otf b/test/api/fonts/MathTestFontPartial2.otf
new file mode 100644 (file)
index 0000000..4607c11
Binary files /dev/null and b/test/api/fonts/MathTestFontPartial2.otf differ
diff --git a/test/api/fonts/MathTestFontPartial3.otf b/test/api/fonts/MathTestFontPartial3.otf
new file mode 100644 (file)
index 0000000..ca18a9a
Binary files /dev/null and b/test/api/fonts/MathTestFontPartial3.otf differ
index 219959b6530541a0206b44cf2b84de03ae490ca3..5ba583f1b8ded904c46fab6d492e4070d89c0403 100644 (file)
@@ -160,6 +160,157 @@ test_get_math_constant (void)
   cleanupFreeType();
 }
 
+static void
+test_get_math_italic_correction (void)
+{
+  hb_codepoint_t glyph;
+  initFreeType();
+
+  openFont("fonts/MathTestFontEmpty.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(hb_ot_layout_get_math_italic_correction (hb_font, glyph), ==, 0); // MathGlyphInfo not available
+  closeFont();
+
+  openFont("fonts/MathTestFontPartial1.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(hb_ot_layout_get_math_italic_correction (hb_font, glyph), ==, 0); // MathGlyphInfo empty
+  closeFont();
+
+  openFont("fonts/MathTestFontPartial2.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(hb_ot_layout_get_math_italic_correction (hb_font, glyph), ==, 0); // MathItalicsCorrectionInfo empty
+  closeFont();
+
+  openFont("fonts/MathTestFontFull.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(hb_ot_layout_get_math_italic_correction (hb_font, glyph), ==, 0); // Glyph without italic correction.
+  g_assert(hb_font_get_glyph_from_name (hb_font, "A", -1, &glyph));
+  g_assert_cmpint(hb_ot_layout_get_math_italic_correction (hb_font, glyph), ==, 197);
+  g_assert(hb_font_get_glyph_from_name (hb_font, "B", -1, &glyph));
+  g_assert_cmpint(hb_ot_layout_get_math_italic_correction (hb_font, glyph), ==, 150);
+  g_assert(hb_font_get_glyph_from_name (hb_font, "C", -1, &glyph));
+  g_assert_cmpint(hb_ot_layout_get_math_italic_correction (hb_font, glyph), ==, 452);
+  closeFont();
+
+  cleanupFreeType();
+
+}
+
+static void
+test_get_math_top_accent_attachment (void)
+{
+  hb_codepoint_t glyph;
+  initFreeType();
+
+  openFont("fonts/MathTestFontEmpty.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(hb_ot_layout_get_math_top_accent_attachment (hb_font, glyph), ==, 0); // MathGlyphInfo not available
+  closeFont();
+
+  openFont("fonts/MathTestFontPartial1.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(hb_ot_layout_get_math_top_accent_attachment (hb_font, glyph), ==, 0); // MathGlyphInfo empty
+  closeFont();
+
+  openFont("fonts/MathTestFontPartial2.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(hb_ot_layout_get_math_top_accent_attachment (hb_font, glyph), ==, 0); // MathTopAccentAttachment empty
+  closeFont();
+
+  openFont("fonts/MathTestFontFull.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(hb_ot_layout_get_math_top_accent_attachment (hb_font, glyph), ==, 0); // Glyph without top accent attachment.
+  g_assert(hb_font_get_glyph_from_name (hb_font, "D", -1, &glyph));
+  g_assert_cmpint(hb_ot_layout_get_math_top_accent_attachment (hb_font, glyph), ==, 374);
+  g_assert(hb_font_get_glyph_from_name (hb_font, "E", -1, &glyph));
+  g_assert_cmpint(hb_ot_layout_get_math_top_accent_attachment (hb_font, glyph), ==, 346);
+  g_assert(hb_font_get_glyph_from_name (hb_font, "F", -1, &glyph));
+  g_assert_cmpint(hb_ot_layout_get_math_top_accent_attachment (hb_font, glyph), ==, 318);
+  closeFont();
+
+  cleanupFreeType();
+}
+
+static void
+test_is_math_extended_shape (void)
+{
+  hb_codepoint_t glyph;
+  initFreeType();
+
+  openFont("fonts/MathTestFontEmpty.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert(!hb_ot_layout_is_math_extended_shape (hb_face, glyph)); // MathGlyphInfo not available
+  closeFont();
+
+  openFont("fonts/MathTestFontPartial1.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert(!hb_ot_layout_is_math_extended_shape (hb_face, glyph)); // MathGlyphInfo empty
+  closeFont();
+
+  openFont("fonts/MathTestFontFull.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "G", -1, &glyph));
+  g_assert(!hb_ot_layout_is_math_extended_shape (hb_face, glyph));
+  g_assert(hb_font_get_glyph_from_name (hb_font, "H", -1, &glyph));
+  g_assert(hb_ot_layout_is_math_extended_shape (hb_face, glyph));
+  closeFont();
+
+  cleanupFreeType();
+}
+
+static void
+test_get_math_kerning (void)
+{
+  hb_codepoint_t glyph;
+  initFreeType();
+
+  openFont("fonts/MathTestFontEmpty.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(hb_ot_layout_get_math_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 0), ==, 0); // MathGlyphInfo not available
+  g_assert_cmpint(hb_ot_layout_get_math_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_LEFT, 0), ==, 0); // MathGlyphInfo not available
+  g_assert_cmpint(hb_ot_layout_get_math_kerning (hb_font, glyph, HB_OT_MATH_KERN_BOTTOM_RIGHT, 0), ==, 0); // MathGlyphInfo not available
+  g_assert_cmpint(hb_ot_layout_get_math_kerning (hb_font, glyph, HB_OT_MATH_KERN_BOTTOM_LEFT, 0), ==, 0); // MathGlyphInfo not available
+  closeFont();
+
+  openFont("fonts/MathTestFontPartial2.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(hb_ot_layout_get_math_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 0), ==, 0); // MathKernInfo empty
+  g_assert_cmpint(hb_ot_layout_get_math_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_LEFT, 0), ==, 0); // MathKernInfo empty
+  g_assert_cmpint(hb_ot_layout_get_math_kerning (hb_font, glyph, HB_OT_MATH_KERN_BOTTOM_RIGHT, 0), ==, 0); // MathKernInfo empty
+  g_assert_cmpint(hb_ot_layout_get_math_kerning (hb_font, glyph, HB_OT_MATH_KERN_BOTTOM_LEFT, 0), ==, 0); // MathKernInfo empty
+  closeFont();
+
+  openFont("fonts/MathTestFontPartial3.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(hb_ot_layout_get_math_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 0), ==, 0); // MathKernInfoRecords empty
+  g_assert_cmpint(hb_ot_layout_get_math_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_LEFT, 0), ==, 0); // MathKernInfoRecords empty
+  g_assert_cmpint(hb_ot_layout_get_math_kerning (hb_font, glyph, HB_OT_MATH_KERN_BOTTOM_RIGHT, 0), ==, 0); // MathKernInfoRecords empty
+  g_assert_cmpint(hb_ot_layout_get_math_kerning (hb_font, glyph, HB_OT_MATH_KERN_BOTTOM_LEFT, 0), ==, 0); // MathKernInfoRecords empty
+  closeFont();
+
+  openFont("fonts/MathTestFontFull.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "I", -1, &glyph));
+
+  g_assert_cmpint(hb_ot_layout_get_math_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 7), ==, 31); // lower than min heigth
+  g_assert_cmpint(hb_ot_layout_get_math_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 14), ==, 31); // equal to min height
+  g_assert_cmpint(hb_ot_layout_get_math_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 20), ==, 52);
+  g_assert_cmpint(hb_ot_layout_get_math_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 23), ==, 52);
+  g_assert_cmpint(hb_ot_layout_get_math_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 31), ==, 73);
+  g_assert_cmpint(hb_ot_layout_get_math_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 32), ==, 73);
+  g_assert_cmpint(hb_ot_layout_get_math_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 86), ==, 199); // equal to max height
+  g_assert_cmpint(hb_ot_layout_get_math_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 91), ==, 220); // larger than max height
+  g_assert_cmpint(hb_ot_layout_get_math_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 96), ==, 220); // larger than max height
+
+  g_assert_cmpint(hb_ot_layout_get_math_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 39), ==, 94); // top right
+  g_assert_cmpint(hb_ot_layout_get_math_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_LEFT, 39), ==, 55); // top left
+  g_assert_cmpint(hb_ot_layout_get_math_kerning (hb_font, glyph, HB_OT_MATH_KERN_BOTTOM_RIGHT, 39), ==, 22); // bottom right
+  g_assert_cmpint(hb_ot_layout_get_math_kerning (hb_font, glyph, HB_OT_MATH_KERN_BOTTOM_LEFT, 39), ==, 50); // bottom left
+
+  closeFont();
+
+  cleanupFreeType();
+}
+
+
 int
 main (int argc, char **argv)
 {
@@ -167,6 +318,10 @@ main (int argc, char **argv)
 
   hb_test_add (test_has_math_data);
   hb_test_add (test_get_math_constant);
+  hb_test_add (test_get_math_italic_correction);
+  hb_test_add (test_get_math_top_accent_attachment);
+  hb_test_add (test_is_math_extended_shape);
+  hb_test_add (test_get_math_kerning);
 
   return hb_test_run();
 }