From 51da7a1cd672aada84bdbb3a2a8dd77ab1134249 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Wang?= Date: Thu, 25 Aug 2016 11:17:50 +0200 Subject: [PATCH] MATH table: Add API to access math variants. --- src/hb-ot-layout-math-table.hh | 253 +++++++++++++++++++++++- src/hb-ot-layout-private.hh | 10 + src/hb-ot-layout.cc | 54 +++++ test/api/fonts/MathTestFontPartial4.otf | Bin 0 -> 14360 bytes test/api/test-ot-layout-math.c | 51 +++++ 5 files changed, 364 insertions(+), 4 deletions(-) create mode 100644 test/api/fonts/MathTestFontPartial4.otf diff --git a/src/hb-ot-layout-math-table.hh b/src/hb-ot-layout-math-table.hh index 7686bf1f..784f5c1a 100644 --- a/src/hb-ot-layout-math-table.hh +++ b/src/hb-ot-layout-math-table.hh @@ -279,7 +279,7 @@ struct MathKern } protected: - USHORT heightCount; + USHORT heightCount; MathValueRecord mathValueRecords[VAR]; /* Array of correction heights at * which the kern value changes. * Sorted by the height value in @@ -413,6 +413,243 @@ public: DEFINE_SIZE_STATIC (8); }; +struct MathGlyphVariantRecord +{ + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + hb_codepoint_t get_glyph() const { return variantGlyph; } + inline hb_position_t get_advance_measurement (hb_font_t *font, + bool horizontal) const + { + return horizontal ? + font->em_scale_x (advanceMeasurement) : + font->em_scale_y (advanceMeasurement); + } + +protected: + GlyphID variantGlyph; /* Glyph ID for the variant. */ + USHORT advanceMeasurement; /* Advance width/height, in design units, of the + * variant, in the direction of requested + * glyph extension. */ + +public: + DEFINE_SIZE_STATIC (2 + 2); +}; + +struct PartFlags : USHORT +{ + enum Flags { + Extender = 0x0001u, /* If set, the part can be skipped or repeated. */ + }; + +public: + DEFINE_SIZE_STATIC (2); +}; + +struct GlyphPartRecord +{ + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + hb_codepoint_t get_glyph() const { return glyph; } + + inline hb_position_t + get_start_connector_length (hb_font_t *font, bool horizontal) const + { + return horizontal ? + font->em_scale_x (startConnectorLength) : + font->em_scale_y (startConnectorLength); + } + + inline hb_position_t + get_end_connector_length (hb_font_t *font, bool horizontal) const + { + return horizontal ? + font->em_scale_x (endConnectorLength) : + font->em_scale_y (endConnectorLength); + } + + inline hb_position_t + get_full_advance (hb_font_t *font, bool horizontal) const + { + return horizontal ? + font->em_scale_x (fullAdvance) : + font->em_scale_y (fullAdvance); + } + + inline bool is_extender() const { + return partFlags & PartFlags::Flags::Extender; + } + +protected: + GlyphID glyph; /* Glyph ID for the part. */ + USHORT startConnectorLength; /* Advance width/ height of the straight bar + * connector material, in design units, is at + * the beginning of the glyph, in the + * direction of the extension. */ + USHORT endConnectorLength; /* Advance width/ height of the straight bar + * connector material, in design units, is at + * the end of the glyph, in the direction of + * the extension. */ + USHORT fullAdvance; /* Full advance width/height for this part, + * in the direction of the extension. + * In design units. */ + PartFlags partFlags; /* Part qualifiers. */ + +public: + DEFINE_SIZE_STATIC (5 * 2); +}; + +struct GlyphAssembly +{ + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + italicsCorrection.sanitize(c, this) && + partRecords.sanitize(c)); + } + + inline hb_position_t get_italic_correction (hb_font_t *font) const + { return italicsCorrection.get_x_value(font, this); } + + inline unsigned int part_record_count() const { return partRecords.len; } + inline const GlyphPartRecord &get_part_record(unsigned int i) const { + assert(i < partRecords.len); + return partRecords[i]; + } + +protected: + MathValueRecord italicsCorrection; /* Italics correction of this + * GlyphAssembly. Should not + * depend on the assembly size. */ + ArrayOf partRecords; /* Array of part records, from + * left to right and bottom to + * top. */ + +public: + DEFINE_SIZE_ARRAY (4 + 2, partRecords); +}; + +struct MathGlyphConstruction +{ + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + glyphAssembly.sanitize(c, this) && + mathGlyphVariantRecord.sanitize(c)); + } + + inline bool has_glyph_assembly (void) const { return glyphAssembly != 0; } + inline const GlyphAssembly &get_glyph_assembly (void) const { + return this+glyphAssembly; + } + + inline unsigned int glyph_variant_count() const { + return mathGlyphVariantRecord.len; + } + inline const MathGlyphVariantRecord &get_glyph_variant(unsigned int i) const { + assert(i < mathGlyphVariantRecord.len); + return mathGlyphVariantRecord[i]; + } + +protected: + /* Offset to GlyphAssembly table for this shape - from the beginning of + MathGlyphConstruction table. May be NULL. */ + OffsetTo glyphAssembly; + + /* MathGlyphVariantRecords for alternative variants of the glyphs. */ + ArrayOf mathGlyphVariantRecord; + +public: + DEFINE_SIZE_ARRAY (2 + 2, mathGlyphVariantRecord); +}; + +struct MathVariants +{ + inline bool sanitize_offsets (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + unsigned int count = vertGlyphCount + horizGlyphCount; + for (unsigned int i = 0; i < count; i++) + if (!glyphConstruction[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) && + vertGlyphCoverage.sanitize (c, this) && + horizGlyphCoverage.sanitize (c, this) && + c->check_array (glyphConstruction, + glyphConstruction[0].static_size, + vertGlyphCount + horizGlyphCount) && + sanitize_offsets (c)); + } + + inline hb_position_t get_min_connector_overlap (hb_font_t *font, + bool horizontal) const + { + return horizontal ? + font->em_scale_x (minConnectorOverlap) : + font->em_scale_y (minConnectorOverlap); + } + + inline bool + get_glyph_construction (hb_codepoint_t glyph, + hb_bool_t horizontal, + const MathGlyphConstruction *&glyph_construction) const { + unsigned int index = + horizontal ? + (this+horizGlyphCoverage).get_coverage (glyph) : + (this+vertGlyphCoverage).get_coverage (glyph); + if (likely (index == NOT_COVERED)) return false; + + USHORT glyphCount = horizontal ? horizGlyphCount : vertGlyphCount; + if (unlikely (index >= glyphCount)) return false; + + if (horizontal) + index += vertGlyphCount; + + glyph_construction = &(this + glyphConstruction[index]); + return true; + } + +protected: + USHORT minConnectorOverlap; /* Minimum overlap of connecting + * glyphs during glyph construction, + * in design units. */ + OffsetTo vertGlyphCoverage; /* Offset to Coverage table - + * from the beginning of MathVariants + * table. */ + OffsetTo horizGlyphCoverage; /* Offset to Coverage table - + * from the beginning of MathVariants + * table. */ + USHORT vertGlyphCount; /* Number of glyphs for which + * information is provided for + * vertically growing variants. */ + USHORT horizGlyphCount; /* Number of glyphs for which + * information is provided for + * horizontally growing variants. */ + + /* Array of offsets to MathGlyphConstruction tables - from the beginning of + the MathVariants table, for shapes growing in vertical/horizontal + direction. */ + OffsetTo glyphConstruction[VAR]; + +public: + DEFINE_SIZE_ARRAY (5 * 2, glyphConstruction); +}; + /* * MATH -- The MATH Table @@ -428,24 +665,32 @@ struct MATH return_trace (version.sanitize (c) && likely (version.major == 1) && mathConstants.sanitize (c, this) && - mathGlyphInfo.sanitize (c, this)); + mathGlyphInfo.sanitize (c, this) && + mathVariants.sanitize (c, this)); } inline hb_position_t get_constant (hb_ot_math_constant_t constant, - hb_font_t *font) const + hb_font_t *font) const { return (this+mathConstants).get_value (constant, font); } inline const MathGlyphInfo &get_math_glyph_info (void) const { return this+mathGlyphInfo; } + + inline bool has_math_variants (void) const { return mathVariants != 0; } + inline const MathVariants &get_math_variants (void) const { + return this+mathVariants; + } + protected: FixedVersion<>version; /* Version of the MATH table * initially set to 0x00010000u */ OffsetTo mathConstants;/* MathConstants table */ OffsetTo mathGlyphInfo;/* MathGlyphInfo table */ + OffsetTo mathVariants; /* MathVariants table */ public: - DEFINE_SIZE_STATIC (8); + DEFINE_SIZE_STATIC (10); }; } /* mathspace OT */ diff --git a/src/hb-ot-layout-private.hh b/src/hb-ot-layout-private.hh index a4272de6..775261a4 100644 --- a/src/hb-ot-layout-private.hh +++ b/src/hb-ot-layout-private.hh @@ -617,5 +617,15 @@ _hb_buffer_assert_gsubgpos_vars (hb_buffer_t *buffer) #undef lig_props #undef glyph_props +namespace OT { + struct MathGlyphConstruction; +}; + +HB_INTERNAL hb_bool_t +hb_ot_layout_get_math_glyph_construction (hb_font_t *font, + hb_codepoint_t glyph, + hb_bool_t horizontal, + hb_position_t &minConnectorOverlap, + const OT::MathGlyphConstruction *&glyph_construction); #endif /* HB_OT_LAYOUT_PRIVATE_HH */ diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc index 3d87b506..e92d38b0 100644 --- a/src/hb-ot-layout.cc +++ b/src/hb-ot-layout.cc @@ -1350,3 +1350,57 @@ hb_ot_layout_get_math_kerning (hb_font_t *font, const OT::MATH &math = _get_math (font->face); return math.get_math_glyph_info().get_kerning (glyph, kern, correction_height, font); } + +#if 0 +/** + * hb_ot_layout_get_math_italic_correction_for_glyph_assembly: + * @font: an #hb_font_t with an OpenType MATH table + * @base_glyph: index of the glyph to stretch + * @horizontal: direction of the stretching + * + * This function tries and get the italic correction associated to the glyph + * assembly used to stretch the base glyph in the specified direction. + * + * Return value: the italic correction of the glyph assembly or 0 + * + * Since: ???? + **/ +HB_EXTERN hb_position_t +hb_ot_layout_get_math_italic_correction_for_glyph_assembly (hb_font_t *font, + hb_codepoint_t base_glyph, + hb_bool_t horizontal) +{ + const OT::MATH &math = _get_math (font->face); + + if (math.has_math_variants()) { + const OT::MathGlyphConstruction* glyph_construction; + if (math.get_math_variants(). + get_glyph_construction(base_glyph, horizontal, glyph_construction) && + glyph_construction->has_glyph_assembly()) + return glyph_construction-> + get_glyph_assembly().get_italic_correction(font); + } + + return 0; +} + +HB_INTERNAL hb_bool_t +hb_ot_layout_get_math_glyph_construction (hb_font_t *font, + hb_codepoint_t glyph, + hb_bool_t horizontal, + hb_position_t &minConnectorOverlap, + const OT::MathGlyphConstruction *&glyph_construction) +{ + const OT::MATH &math = _get_math (font->face); + + if (!math.has_math_variants()) return false; + + const OT::MathVariants &mathVariants = math.get_math_variants(); + if (!mathVariants.get_glyph_construction(glyph, horizontal, + glyph_construction)) return false; + + minConnectorOverlap = mathVariants.get_min_connector_overlap(font, horizontal); + + return true; +} +#endif diff --git a/test/api/fonts/MathTestFontPartial4.otf b/test/api/fonts/MathTestFontPartial4.otf new file mode 100644 index 0000000000000000000000000000000000000000..cda3057f8360d61154b3fad650c77e413e3d97f0 GIT binary patch literal 14360 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 OKcR?G6mRw~