/* * Copyright © 2016 Igalia S.L. * * This is part of HarfBuzz, a text shaping library. * * Permission is hereby granted, without written agreement and without * license or royalty fees, to use, copy, modify, and distribute this * software and its documentation for any purpose, provided that the * above copyright notice and the following two paragraphs appear in * all copies of this software. * * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * * Igalia Author(s): Frédéric Wang */ #ifndef HB_OT_MATH_TABLE_HH #define HB_OT_MATH_TABLE_HH #include "hb-open-type.hh" #include "hb-ot-layout-common.hh" #include "hb-ot-math.h" namespace OT { struct MathValueRecord { hb_position_t get_x_value (hb_font_t *font, const void *base) const { return font->em_scale_x (value) + (base+deviceTable).get_x_delta (font); } hb_position_t get_y_value (hb_font_t *font, const void *base) const { return font->em_scale_y (value) + (base+deviceTable).get_y_delta (font); } MathValueRecord* copy (hb_serialize_context_t *c, const void *base) const { TRACE_SERIALIZE (this); auto *out = c->embed (this); if (unlikely (!out)) return_trace (nullptr); out->deviceTable.serialize_copy (c, deviceTable, base, 0, hb_serialize_context_t::Head); return_trace (out); } bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && deviceTable.sanitize (c, base)); } protected: HBINT16 value; /* The X or Y value in design units */ Offset16To deviceTable; /* Offset to the device table - from the * beginning of parent table. May be NULL. * Suggested format for device table is 1. */ public: DEFINE_SIZE_STATIC (4); }; struct MathConstants { MathConstants* copy (hb_serialize_context_t *c) const { TRACE_SERIALIZE (this); auto *out = c->start_embed (this); if (unlikely (!out)) return_trace (nullptr); HBINT16 *p = c->allocate_size (HBINT16::static_size * 2); if (unlikely (!p)) return_trace (nullptr); hb_memcpy (p, percentScaleDown, HBINT16::static_size * 2); HBUINT16 *m = c->allocate_size (HBUINT16::static_size * 2); if (unlikely (!m)) return_trace (nullptr); hb_memcpy (m, minHeight, HBUINT16::static_size * 2); unsigned count = ARRAY_LENGTH (mathValueRecords); for (unsigned i = 0; i < count; i++) if (!c->copy (mathValueRecords[i], this)) return_trace (nullptr); if (!c->embed (radicalDegreeBottomRaisePercent)) return_trace (nullptr); return_trace (out); } bool sanitize_math_value_records (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); unsigned int count = ARRAY_LENGTH (mathValueRecords); for (unsigned int i = 0; i < count; i++) if (!mathValueRecords[i].sanitize (c, this)) return_trace (false); return_trace (true); } bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && sanitize_math_value_records (c)); } hb_position_t get_value (hb_ot_math_constant_t constant, hb_font_t *font) const { switch (constant) { case HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN: case HB_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN: return percentScaleDown[constant - HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN]; case HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT: case HB_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT: return font->em_scale_y (minHeight[constant - HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT]); case HB_OT_MATH_CONSTANT_RADICAL_KERN_AFTER_DEGREE: case HB_OT_MATH_CONSTANT_RADICAL_KERN_BEFORE_DEGREE: case HB_OT_MATH_CONSTANT_SKEWED_FRACTION_HORIZONTAL_GAP: case HB_OT_MATH_CONSTANT_SPACE_AFTER_SCRIPT: return mathValueRecords[constant - HB_OT_MATH_CONSTANT_MATH_LEADING].get_x_value (font, this); case HB_OT_MATH_CONSTANT_ACCENT_BASE_HEIGHT: case HB_OT_MATH_CONSTANT_AXIS_HEIGHT: case HB_OT_MATH_CONSTANT_FLATTENED_ACCENT_BASE_HEIGHT: case HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_DISPLAY_STYLE_SHIFT_DOWN: case HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_GAP_MIN: case HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_SHIFT_DOWN: case HB_OT_MATH_CONSTANT_FRACTION_DENOM_DISPLAY_STYLE_GAP_MIN: case HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_DISPLAY_STYLE_SHIFT_UP: case HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_GAP_MIN: case HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_SHIFT_UP: case HB_OT_MATH_CONSTANT_FRACTION_NUM_DISPLAY_STYLE_GAP_MIN: case HB_OT_MATH_CONSTANT_FRACTION_RULE_THICKNESS: case HB_OT_MATH_CONSTANT_LOWER_LIMIT_BASELINE_DROP_MIN: case HB_OT_MATH_CONSTANT_LOWER_LIMIT_GAP_MIN: case HB_OT_MATH_CONSTANT_MATH_LEADING: case HB_OT_MATH_CONSTANT_OVERBAR_EXTRA_ASCENDER: case HB_OT_MATH_CONSTANT_OVERBAR_RULE_THICKNESS: case HB_OT_MATH_CONSTANT_OVERBAR_VERTICAL_GAP: case HB_OT_MATH_CONSTANT_RADICAL_DISPLAY_STYLE_VERTICAL_GAP: case HB_OT_MATH_CONSTANT_RADICAL_EXTRA_ASCENDER: case HB_OT_MATH_CONSTANT_RADICAL_RULE_THICKNESS: case HB_OT_MATH_CONSTANT_RADICAL_VERTICAL_GAP: case HB_OT_MATH_CONSTANT_SKEWED_FRACTION_VERTICAL_GAP: case HB_OT_MATH_CONSTANT_STACK_BOTTOM_DISPLAY_STYLE_SHIFT_DOWN: case HB_OT_MATH_CONSTANT_STACK_BOTTOM_SHIFT_DOWN: case HB_OT_MATH_CONSTANT_STACK_DISPLAY_STYLE_GAP_MIN: case HB_OT_MATH_CONSTANT_STACK_GAP_MIN: case HB_OT_MATH_CONSTANT_STACK_TOP_DISPLAY_STYLE_SHIFT_UP: case HB_OT_MATH_CONSTANT_STACK_TOP_SHIFT_UP: case HB_OT_MATH_CONSTANT_STRETCH_STACK_BOTTOM_SHIFT_DOWN: case HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_ABOVE_MIN: case HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_BELOW_MIN: case HB_OT_MATH_CONSTANT_STRETCH_STACK_TOP_SHIFT_UP: case HB_OT_MATH_CONSTANT_SUBSCRIPT_BASELINE_DROP_MIN: case HB_OT_MATH_CONSTANT_SUBSCRIPT_SHIFT_DOWN: case HB_OT_MATH_CONSTANT_SUBSCRIPT_TOP_MAX: case HB_OT_MATH_CONSTANT_SUB_SUPERSCRIPT_GAP_MIN: case HB_OT_MATH_CONSTANT_SUPERSCRIPT_BASELINE_DROP_MAX: case HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MAX_WITH_SUBSCRIPT: case HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MIN: case HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP: case HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP_CRAMPED: case HB_OT_MATH_CONSTANT_UNDERBAR_EXTRA_DESCENDER: case HB_OT_MATH_CONSTANT_UNDERBAR_RULE_THICKNESS: case HB_OT_MATH_CONSTANT_UNDERBAR_VERTICAL_GAP: case HB_OT_MATH_CONSTANT_UPPER_LIMIT_BASELINE_RISE_MIN: case HB_OT_MATH_CONSTANT_UPPER_LIMIT_GAP_MIN: return mathValueRecords[constant - HB_OT_MATH_CONSTANT_MATH_LEADING].get_y_value (font, this); case HB_OT_MATH_CONSTANT_RADICAL_DEGREE_BOTTOM_RAISE_PERCENT: return radicalDegreeBottomRaisePercent; default: return 0; } } protected: HBINT16 percentScaleDown[2]; HBUINT16 minHeight[2]; MathValueRecord mathValueRecords[51]; HBINT16 radicalDegreeBottomRaisePercent; public: DEFINE_SIZE_STATIC (214); }; struct MathItalicsCorrectionInfo { bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); const hb_set_t &glyphset = c->plan->_glyphset_mathed; const hb_map_t &glyph_map = *c->plan->glyph_map; auto *out = c->serializer->start_embed (*this); if (unlikely (!c->serializer->extend_min (out))) return_trace (false); hb_sorted_vector_t new_coverage; + hb_zip (this+coverage, italicsCorrection) | hb_filter (glyphset, hb_first) | hb_filter (serialize_math_record_array (c->serializer, out->italicsCorrection, this), hb_second) | hb_map (hb_first) | hb_map (glyph_map) | hb_sink (new_coverage) ; out->coverage.serialize_serialize (c->serializer, new_coverage.iter ()); return_trace (true); } 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)); } hb_position_t get_value (hb_codepoint_t glyph, hb_font_t *font) const { unsigned int index = (this+coverage).get_coverage (glyph); return italicsCorrection[index].get_x_value (font, this); } protected: Offset16To coverage; /* Offset to Coverage table - * from the beginning of * MathItalicsCorrectionInfo * table. */ Array16Of italicsCorrection; /* Array of MathValueRecords * defining italics correction * values for each * covered glyph. */ public: DEFINE_SIZE_ARRAY (4, italicsCorrection); }; struct MathTopAccentAttachment { bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); const hb_set_t &glyphset = c->plan->_glyphset_mathed; const hb_map_t &glyph_map = *c->plan->glyph_map; auto *out = c->serializer->start_embed (*this); if (unlikely (!c->serializer->extend_min (out))) return_trace (false); hb_sorted_vector_t new_coverage; + hb_zip (this+topAccentCoverage, topAccentAttachment) | hb_filter (glyphset, hb_first) | hb_filter (serialize_math_record_array (c->serializer, out->topAccentAttachment, this), hb_second) | hb_map (hb_first) | hb_map (glyph_map) | hb_sink (new_coverage) ; out->topAccentCoverage.serialize_serialize (c->serializer, new_coverage.iter ()); return_trace (true); } 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)); } hb_position_t get_value (hb_codepoint_t glyph, hb_font_t *font) const { unsigned int index = (this+topAccentCoverage).get_coverage (glyph); if (index == NOT_COVERED) return font->get_glyph_h_advance (glyph) / 2; return topAccentAttachment[index].get_x_value (font, this); } protected: Offset16To topAccentCoverage; /* Offset to Coverage table - * from the beginning of * MathTopAccentAttachment * table. */ Array16Of topAccentAttachment; /* Array of MathValueRecords * defining top accent * attachment points for each * covered glyph. */ public: DEFINE_SIZE_ARRAY (2 + 2, topAccentAttachment); }; struct MathKern { MathKern* copy (hb_serialize_context_t *c) const { TRACE_SERIALIZE (this); auto *out = c->start_embed (this); if (unlikely (!out)) return_trace (nullptr); if (unlikely (!c->embed (heightCount))) return_trace (nullptr); unsigned count = 2 * heightCount + 1; for (unsigned i = 0; i < count; i++) if (!c->copy (mathValueRecordsZ.arrayZ[i], this)) return_trace (nullptr); return_trace (out); } 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 (!mathValueRecordsZ.arrayZ[i].sanitize (c, this)) return_trace (false); return_trace (true); } bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && c->check_array (mathValueRecordsZ.arrayZ, 2 * heightCount + 1) && sanitize_math_value_records (c)); } hb_position_t get_value (hb_position_t correction_height, hb_font_t *font) const { const MathValueRecord* correctionHeight = mathValueRecordsZ.arrayZ; const MathValueRecord* kernValue = mathValueRecordsZ.arrayZ + heightCount; int sign = font->y_scale < 0 ? -1 : +1; /* 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 i = 0; unsigned int count = heightCount; while (count > 0) { unsigned int half = count / 2; hb_position_t height = correctionHeight[i + half].get_y_value (font, this); if (sign * height < sign * correction_height) { i += half + 1; count -= half + 1; } else count = half; } return kernValue[i].get_x_value (font, this); } unsigned int get_entries (unsigned int start_offset, unsigned int *entries_count, /* IN/OUT */ hb_ot_math_kern_entry_t *kern_entries, /* OUT */ hb_font_t *font) const { const MathValueRecord* correctionHeight = mathValueRecordsZ.arrayZ; const MathValueRecord* kernValue = mathValueRecordsZ.arrayZ + heightCount; const unsigned int entriesCount = heightCount + 1; if (entries_count) { unsigned int start = hb_min (start_offset, entriesCount); unsigned int end = hb_min (start + *entries_count, entriesCount); *entries_count = end - start; for (unsigned int i = 0; i < *entries_count; i++) { unsigned int j = start + i; hb_position_t max_height; if (j == heightCount) { max_height = INT32_MAX; } else { max_height = correctionHeight[j].get_y_value (font, this); } kern_entries[i] = {max_height, kernValue[j].get_x_value (font, this)}; } } return entriesCount; } protected: HBUINT16 heightCount; UnsizedArrayOf mathValueRecordsZ; /* Array of correction heights at * which the kern value changes. * Sorted by the height value in * design units (heightCount entries), * Followed by: * Array of kern values corresponding * to heights. (heightCount+1 entries). */ public: DEFINE_SIZE_ARRAY (2, mathValueRecordsZ); }; struct MathKernInfoRecord { MathKernInfoRecord* copy (hb_serialize_context_t *c, const void *base) const { TRACE_SERIALIZE (this); auto *out = c->embed (this); if (unlikely (!out)) return_trace (nullptr); unsigned count = ARRAY_LENGTH (mathKern); for (unsigned i = 0; i < count; i++) out->mathKern[i].serialize_copy (c, mathKern[i], base, 0, hb_serialize_context_t::Head); return_trace (out); } bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); unsigned int count = ARRAY_LENGTH (mathKern); for (unsigned int i = 0; i < count; i++) if (unlikely (!mathKern[i].sanitize (c, base))) return_trace (false); return_trace (true); } hb_position_t get_kerning (hb_ot_math_kern_t kern, hb_position_t correction_height, hb_font_t *font, const void *base) const { unsigned int idx = kern; if (unlikely (idx >= ARRAY_LENGTH (mathKern))) return 0; return (base+mathKern[idx]).get_value (correction_height, font); } unsigned int get_kernings (hb_ot_math_kern_t kern, unsigned int start_offset, unsigned int *entries_count, /* IN/OUT */ hb_ot_math_kern_entry_t *kern_entries, /* OUT */ hb_font_t *font, const void *base) const { unsigned int idx = kern; if (unlikely (idx >= ARRAY_LENGTH (mathKern)) || !mathKern[idx]) { if (entries_count) *entries_count = 0; return 0; } return (base+mathKern[idx]).get_entries (start_offset, entries_count, kern_entries, font); } protected: /* Offset to MathKern table for each corner - * from the beginning of MathKernInfo table. May be NULL. */ Offset16To mathKern[4]; public: DEFINE_SIZE_STATIC (8); }; struct MathKernInfo { bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); const hb_set_t &glyphset = c->plan->_glyphset_mathed; const hb_map_t &glyph_map = *c->plan->glyph_map; auto *out = c->serializer->start_embed (*this); if (unlikely (!c->serializer->extend_min (out))) return_trace (false); hb_sorted_vector_t new_coverage; + hb_zip (this+mathKernCoverage, mathKernInfoRecords) | hb_filter (glyphset, hb_first) | hb_filter (serialize_math_record_array (c->serializer, out->mathKernInfoRecords, this), hb_second) | hb_map (hb_first) | hb_map (glyph_map) | hb_sink (new_coverage) ; out->mathKernCoverage.serialize_serialize (c->serializer, new_coverage.iter ()); return_trace (true); } 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)); } hb_position_t get_kerning (hb_codepoint_t glyph, hb_ot_math_kern_t kern, hb_position_t correction_height, hb_font_t *font) const { unsigned int index = (this+mathKernCoverage).get_coverage (glyph); return mathKernInfoRecords[index].get_kerning (kern, correction_height, font, this); } unsigned int get_kernings (hb_codepoint_t glyph, hb_ot_math_kern_t kern, unsigned int start_offset, unsigned int *entries_count, /* IN/OUT */ hb_ot_math_kern_entry_t *kern_entries, /* OUT */ hb_font_t *font) const { unsigned int index = (this+mathKernCoverage).get_coverage (glyph); return mathKernInfoRecords[index].get_kernings (kern, start_offset, entries_count, kern_entries, font, this); } protected: Offset16To mathKernCoverage; /* Offset to Coverage table - * from the beginning of the * MathKernInfo table. */ Array16Of mathKernInfoRecords; /* Array of MathKernInfoRecords, * per-glyph information for * mathematical positioning * of subscripts and * superscripts. */ public: DEFINE_SIZE_ARRAY (4, mathKernInfoRecords); }; struct MathGlyphInfo { bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); auto *out = c->serializer->embed (*this); if (unlikely (!out)) return_trace (false); out->mathItalicsCorrectionInfo.serialize_subset (c, mathItalicsCorrectionInfo, this); out->mathTopAccentAttachment.serialize_subset (c, mathTopAccentAttachment, this); const hb_set_t &glyphset = c->plan->_glyphset_mathed; const hb_map_t &glyph_map = *c->plan->glyph_map; auto it = + hb_iter (this+extendedShapeCoverage) | hb_filter (glyphset) | hb_map_retains_sorting (glyph_map) ; if (it) out->extendedShapeCoverage.serialize_serialize (c->serializer, it); else out->extendedShapeCoverage = 0; out->mathKernInfo.serialize_subset (c, mathKernInfo, this); return_trace (true); } 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)); } hb_position_t get_italics_correction (hb_codepoint_t glyph, hb_font_t *font) const { return (this+mathItalicsCorrectionInfo).get_value (glyph, font); } hb_position_t get_top_accent_attachment (hb_codepoint_t glyph, hb_font_t *font) const { return (this+mathTopAccentAttachment).get_value (glyph, font); } bool is_extended_shape (hb_codepoint_t glyph) const { return (this+extendedShapeCoverage).get_coverage (glyph) != NOT_COVERED; } hb_position_t get_kerning (hb_codepoint_t glyph, hb_ot_math_kern_t kern, hb_position_t correction_height, hb_font_t *font) const { return (this+mathKernInfo).get_kerning (glyph, kern, correction_height, font); } hb_position_t get_kernings (hb_codepoint_t glyph, hb_ot_math_kern_t kern, unsigned int start_offset, unsigned int *entries_count, /* IN/OUT */ hb_ot_math_kern_entry_t *kern_entries, /* OUT */ hb_font_t *font) const { return (this+mathKernInfo).get_kernings (glyph, kern, start_offset, entries_count, kern_entries, font); } protected: /* Offset to MathItalicsCorrectionInfo table - * from the beginning of MathGlyphInfo table. */ Offset16To mathItalicsCorrectionInfo; /* Offset to MathTopAccentAttachment table - * from the beginning of MathGlyphInfo table. */ Offset16To 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.. */ Offset16To extendedShapeCoverage; /* Offset to MathKernInfo table - * from the beginning of MathGlyphInfo table. */ Offset16To mathKernInfo; public: DEFINE_SIZE_STATIC (8); }; struct MathGlyphVariantRecord { friend struct MathGlyphConstruction; bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); auto *out = c->serializer->embed (this); if (unlikely (!out)) return_trace (false); const hb_map_t& glyph_map = *c->plan->glyph_map; return_trace (c->serializer->check_assign (out->variantGlyph, glyph_map.get (variantGlyph), HB_SERIALIZE_ERROR_INT_OVERFLOW)); } bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this)); } void closure_glyphs (hb_set_t *variant_glyphs) const { variant_glyphs->add (variantGlyph); } protected: HBGlyphID16 variantGlyph; /* Glyph ID for the variant. */ HBUINT16 advanceMeasurement; /* Advance width/height, in design units, of the * variant, in the direction of requested * glyph extension. */ public: DEFINE_SIZE_STATIC (4); }; struct PartFlags : HBUINT16 { enum Flags { Extender = 0x0001u, /* If set, the part can be skipped or repeated. */ Defined = 0x0001u, /* All defined flags. */ }; public: DEFINE_SIZE_STATIC (2); }; struct MathGlyphPartRecord { bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); auto *out = c->serializer->embed (this); if (unlikely (!out)) return_trace (false); const hb_map_t& glyph_map = *c->plan->glyph_map; return_trace (c->serializer->check_assign (out->glyph, glyph_map.get (glyph), HB_SERIALIZE_ERROR_INT_OVERFLOW)); } bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this)); } void extract (hb_ot_math_glyph_part_t &out, int64_t mult, hb_font_t *font) const { out.glyph = glyph; out.start_connector_length = font->em_mult (startConnectorLength, mult); out.end_connector_length = font->em_mult (endConnectorLength, mult); out.full_advance = font->em_mult (fullAdvance, mult); static_assert ((unsigned int) HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER == (unsigned int) PartFlags::Extender, ""); out.flags = (hb_ot_math_glyph_part_flags_t) (unsigned int) (partFlags & PartFlags::Defined); } void closure_glyphs (hb_set_t *variant_glyphs) const { variant_glyphs->add (glyph); } protected: HBGlyphID16 glyph; /* Glyph ID for the part. */ HBUINT16 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. */ HBUINT16 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. */ HBUINT16 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 (10); }; struct MathGlyphAssembly { bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); auto *out = c->serializer->start_embed (*this); if (unlikely (!out)) return_trace (false); if (!c->serializer->copy (italicsCorrection, this)) return_trace (false); if (!c->serializer->copy (partRecords.len)) return_trace (false); for (const auto& record : partRecords.iter ()) if (!record.subset (c)) return_trace (false); return_trace (true); } bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && italicsCorrection.sanitize (c, this) && partRecords.sanitize (c)); } unsigned int get_parts (hb_direction_t direction, hb_font_t *font, unsigned int start_offset, unsigned int *parts_count, /* IN/OUT */ hb_ot_math_glyph_part_t *parts /* OUT */, hb_position_t *italics_correction /* OUT */) const { if (parts_count) { int64_t mult = font->dir_mult (direction); for (auto _ : hb_zip (partRecords.as_array ().sub_array (start_offset, parts_count), hb_array (parts, *parts_count))) _.first.extract (_.second, mult, font); } if (italics_correction) *italics_correction = italicsCorrection.get_x_value (font, this); return partRecords.len; } void closure_glyphs (hb_set_t *variant_glyphs) const { for (const auto& _ : partRecords.iter ()) _.closure_glyphs (variant_glyphs); } protected: MathValueRecord italicsCorrection; /* Italics correction of this * MathGlyphAssembly. Should not * depend on the assembly size. */ Array16Of partRecords; /* Array of part records, from * left to right and bottom to * top. */ public: DEFINE_SIZE_ARRAY (6, partRecords); }; struct MathGlyphConstruction { bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); auto *out = c->serializer->start_embed (*this); if (unlikely (!c->serializer->extend_min (out))) return_trace (false); out->glyphAssembly.serialize_subset (c, glyphAssembly, this); if (!c->serializer->check_assign (out->mathGlyphVariantRecord.len, mathGlyphVariantRecord.len, HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false); for (const auto& record : mathGlyphVariantRecord.iter ()) if (!record.subset (c)) return_trace (false); return_trace (true); } bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && glyphAssembly.sanitize (c, this) && mathGlyphVariantRecord.sanitize (c)); } const MathGlyphAssembly &get_assembly () const { return this+glyphAssembly; } unsigned int get_variants (hb_direction_t direction, hb_font_t *font, unsigned int start_offset, unsigned int *variants_count, /* IN/OUT */ hb_ot_math_glyph_variant_t *variants /* OUT */) const { if (variants_count) { int64_t mult = font->dir_mult (direction); for (auto _ : hb_zip (mathGlyphVariantRecord.as_array ().sub_array (start_offset, variants_count), hb_array (variants, *variants_count))) _.second = {_.first.variantGlyph, font->em_mult (_.first.advanceMeasurement, mult)}; } return mathGlyphVariantRecord.len; } void closure_glyphs (hb_set_t *variant_glyphs) const { (this+glyphAssembly).closure_glyphs (variant_glyphs); for (const auto& _ : mathGlyphVariantRecord.iter ()) _.closure_glyphs (variant_glyphs); } protected: /* Offset to MathGlyphAssembly table for this shape - from the beginning of MathGlyphConstruction table. May be NULL. */ Offset16To glyphAssembly; /* MathGlyphVariantRecords for alternative variants of the glyphs. */ Array16Of mathGlyphVariantRecord; public: DEFINE_SIZE_ARRAY (4, mathGlyphVariantRecord); }; struct MathVariants { void closure_glyphs (const hb_set_t *glyph_set, hb_set_t *variant_glyphs) const { const hb_array_t> glyph_construction_offsets = glyphConstruction.as_array (vertGlyphCount + horizGlyphCount); if (vertGlyphCoverage) { const auto vert_offsets = glyph_construction_offsets.sub_array (0, vertGlyphCount); + hb_zip (this+vertGlyphCoverage, vert_offsets) | hb_filter (glyph_set, hb_first) | hb_map (hb_second) | hb_map (hb_add (this)) | hb_apply ([=] (const MathGlyphConstruction &_) { _.closure_glyphs (variant_glyphs); }) ; } if (horizGlyphCoverage) { const auto hori_offsets = glyph_construction_offsets.sub_array (vertGlyphCount, horizGlyphCount); + hb_zip (this+horizGlyphCoverage, hori_offsets) | hb_filter (glyph_set, hb_first) | hb_map (hb_second) | hb_map (hb_add (this)) | hb_apply ([=] (const MathGlyphConstruction &_) { _.closure_glyphs (variant_glyphs); }) ; } } void collect_coverage_and_indices (hb_sorted_vector_t& new_coverage, const Offset16To& coverage, unsigned i, unsigned end_index, hb_set_t& indices, const hb_set_t& glyphset, const hb_map_t& glyph_map) const { if (!coverage) return; for (const auto _ : (this+coverage).iter ()) { if (i >= end_index) return; if (glyphset.has (_)) { unsigned new_gid = glyph_map.get (_); new_coverage.push (new_gid); indices.add (i); } i++; } } bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); const hb_set_t &glyphset = c->plan->_glyphset_mathed; const hb_map_t &glyph_map = *c->plan->glyph_map; auto *out = c->serializer->start_embed (*this); if (unlikely (!c->serializer->extend_min (out))) return_trace (false); if (!c->serializer->check_assign (out->minConnectorOverlap, minConnectorOverlap, HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false); hb_sorted_vector_t new_vert_coverage; hb_sorted_vector_t new_hori_coverage; hb_set_t indices; collect_coverage_and_indices (new_vert_coverage, vertGlyphCoverage, 0, vertGlyphCount, indices, glyphset, glyph_map); collect_coverage_and_indices (new_hori_coverage, horizGlyphCoverage, vertGlyphCount, vertGlyphCount + horizGlyphCount, indices, glyphset, glyph_map); if (!c->serializer->check_assign (out->vertGlyphCount, new_vert_coverage.length, HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false); if (!c->serializer->check_assign (out->horizGlyphCount, new_hori_coverage.length, HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false); for (unsigned i : indices.iter ()) { auto *o = c->serializer->embed (glyphConstruction[i]); if (!o) return_trace (false); o->serialize_subset (c, glyphConstruction[i], this); } if (new_vert_coverage) out->vertGlyphCoverage.serialize_serialize (c->serializer, new_vert_coverage.iter ()); if (new_hori_coverage) out->horizGlyphCoverage.serialize_serialize (c->serializer, new_hori_coverage.iter ()); return_trace (true); } 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.arrayZ[i].sanitize (c, this)) return_trace (false); return_trace (true); } 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.arrayZ, vertGlyphCount + horizGlyphCount) && sanitize_offsets (c)); } hb_position_t get_min_connector_overlap (hb_direction_t direction, hb_font_t *font) const { return font->em_scale_dir (minConnectorOverlap, direction); } unsigned int get_glyph_variants (hb_codepoint_t glyph, hb_direction_t direction, hb_font_t *font, unsigned int start_offset, unsigned int *variants_count, /* IN/OUT */ hb_ot_math_glyph_variant_t *variants /* OUT */) const { return get_glyph_construction (glyph, direction, font) .get_variants (direction, font, start_offset, variants_count, variants); } unsigned int get_glyph_parts (hb_codepoint_t glyph, hb_direction_t direction, hb_font_t *font, unsigned int start_offset, unsigned int *parts_count, /* IN/OUT */ hb_ot_math_glyph_part_t *parts /* OUT */, hb_position_t *italics_correction /* OUT */) const { return get_glyph_construction (glyph, direction, font) .get_assembly () .get_parts (direction, font, start_offset, parts_count, parts, italics_correction); } private: const MathGlyphConstruction & get_glyph_construction (hb_codepoint_t glyph, hb_direction_t direction, hb_font_t *font HB_UNUSED) const { bool vertical = HB_DIRECTION_IS_VERTICAL (direction); unsigned int count = vertical ? vertGlyphCount : horizGlyphCount; const Offset16To &coverage = vertical ? vertGlyphCoverage : horizGlyphCoverage; unsigned int index = (this+coverage).get_coverage (glyph); if (unlikely (index >= count)) return Null (MathGlyphConstruction); if (!vertical) index += vertGlyphCount; return this+glyphConstruction[index]; } protected: HBUINT16 minConnectorOverlap; /* Minimum overlap of connecting * glyphs during glyph construction, * in design units. */ Offset16To vertGlyphCoverage; /* Offset to Coverage table - * from the beginning of MathVariants * table. */ Offset16To horizGlyphCoverage; /* Offset to Coverage table - * from the beginning of MathVariants * table. */ HBUINT16 vertGlyphCount; /* Number of glyphs for which * information is provided for * vertically growing variants. */ HBUINT16 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. */ UnsizedArrayOf> glyphConstruction; public: DEFINE_SIZE_ARRAY (10, glyphConstruction); }; /* * MATH -- Mathematical typesetting * https://docs.microsoft.com/en-us/typography/opentype/spec/math */ struct MATH { static constexpr hb_tag_t tableTag = HB_OT_TAG_MATH; bool has_data () const { return version.to_int (); } void closure_glyphs (hb_set_t *glyph_set) const { if (mathVariants) { hb_set_t variant_glyphs; (this+mathVariants).closure_glyphs (glyph_set, &variant_glyphs); hb_set_union (glyph_set, &variant_glyphs); } } bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); auto *out = c->serializer->embed (*this); if (unlikely (!out)) return_trace (false); out->mathConstants.serialize_copy (c->serializer, mathConstants, this, 0, hb_serialize_context_t::Head); out->mathGlyphInfo.serialize_subset (c, mathGlyphInfo, this); out->mathVariants.serialize_subset (c, mathVariants, this); return_trace (true); } bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (version.sanitize (c) && likely (version.major == 1) && mathConstants.sanitize (c, this) && mathGlyphInfo.sanitize (c, this) && mathVariants.sanitize (c, this)); } hb_position_t get_constant (hb_ot_math_constant_t constant, hb_font_t *font) const { return (this+mathConstants).get_value (constant, font); } const MathGlyphInfo &get_glyph_info () const { return this+mathGlyphInfo; } const MathVariants &get_variants () const { return this+mathVariants; } protected: FixedVersion<>version; /* Version of the MATH table * initially set to 0x00010000u */ Offset16To mathConstants; /* MathConstants table */ Offset16To mathGlyphInfo; /* MathGlyphInfo table */ Offset16To mathVariants; /* MathVariants table */ public: DEFINE_SIZE_STATIC (10); }; } /* namespace OT */ #endif /* HB_OT_MATH_TABLE_HH */