From d7182d129612c619b9026ee9b15f2dcfada132db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Wang?= Date: Thu, 25 Aug 2016 11:15:31 +0200 Subject: [PATCH] MATH Table: Add API to access glyph info. --- src/hb-ot-layout-math-table.hh | 271 +++++++++++++++++++++++- src/hb-ot-layout.cc | 114 ++++++++++ src/hb-ot-layout.h | 18 ++ src/hb-ot-math.h | 7 + test/api/fonts/MathTestFontPartial1.otf | Bin 0 -> 14348 bytes test/api/fonts/MathTestFontPartial2.otf | Bin 0 -> 14356 bytes test/api/fonts/MathTestFontPartial3.otf | Bin 0 -> 14380 bytes test/api/test-ot-layout-math.c | 155 ++++++++++++++ 8 files changed, 563 insertions(+), 2 deletions(-) create mode 100644 test/api/fonts/MathTestFontPartial1.otf create mode 100644 test/api/fonts/MathTestFontPartial2.otf create mode 100644 test/api/fonts/MathTestFontPartial3.otf diff --git a/src/hb-ot-layout-math-table.hh b/src/hb-ot-layout-math-table.hh index e65680b5a..2eb0d9228 100644 --- a/src/hb-ot-layout-math-table.hh +++ b/src/hb-ot-layout-math-table.hh @@ -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; /* Offset to Coverage table - + from the beginning of + MathItalicsCorrectionInfo + table. */ + ArrayOf 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 topAccentCoverage; /* Offset to Coverage table - + from the beginning of + MathTopAccentAttachment + table. */ + ArrayOf 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[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 mathKernCoverage; /* Offset to Coverage table - + from the beginning of the + MathKernInfo table. */ + ArrayOf 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; + + /* Offset to MathTopAccentAttachment table - + from the beginning of MathGlyphInfo table. */ + OffsetTo 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 extendedShapeCoverage; + + /* Offset to MathKernInfo table - + from the beginning of MathGlyphInfo table. */ + OffsetTo 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 table */ + OffsetTo mathGlyphInfo; /* MathGlyphInfo table */ public: - DEFINE_SIZE_STATIC (6); + DEFINE_SIZE_STATIC (8); }; } /* mathspace OT */ diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc index 2ae138876..f14611b47 100644 --- a/src/hb-ot-layout.cc +++ b/src/hb-ot-layout.cc @@ -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; +} diff --git a/src/hb-ot-layout.h b/src/hb-ot-layout.h index 501082e28..ffb6321c7 100644 --- a/src/hb-ot-layout.h +++ b/src/hb-ot-layout.h @@ -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 diff --git a/src/hb-ot-math.h b/src/hb-ot-math.h index 7f0ec2713..a62b4b612 100644 --- a/src/hb-ot-math.h +++ b/src/hb-ot-math.h @@ -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 index 0000000000000000000000000000000000000000..b3bf36e33efb8a5b0f4f33f74f952fb26f34c42c GIT binary patch literal 14348 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 P{^9TW@B^CmujBp)n>Hxn literal 0 HcmV?d00001 diff --git a/test/api/fonts/MathTestFontPartial3.otf b/test/api/fonts/MathTestFontPartial3.otf new file mode 100644 index 0000000000000000000000000000000000000000..ca18a9a228facf9472f09122d6646eeeb3a0ff86 GIT binary patch literal 14380 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 a{<`%z^t-37;b#LsZ{UZYnTzdT&HWOy+$iAy literal 0 HcmV?d00001 diff --git a/test/api/test-ot-layout-math.c b/test/api/test-ot-layout-math.c index 219959b65..5ba583f1b 100644 --- a/test/api/test-ot-layout-math.c +++ b/test/api/test-ot-layout-math.c @@ -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(); }