1140 lines
36 KiB
C++
1140 lines
36 KiB
C++
/*
|
|
* 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<Device> 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> (HBINT16::static_size * 2);
|
|
if (unlikely (!p)) return_trace (nullptr);
|
|
hb_memcpy (p, percentScaleDown, HBINT16::static_size * 2);
|
|
|
|
HBUINT16 *m = c->allocate_size<HBUINT16> (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<hb_codepoint_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> coverage; /* Offset to Coverage table -
|
|
* from the beginning of
|
|
* MathItalicsCorrectionInfo
|
|
* table. */
|
|
Array16Of<MathValueRecord> 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<hb_codepoint_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<Coverage> topAccentCoverage; /* Offset to Coverage table -
|
|
* from the beginning of
|
|
* MathTopAccentAttachment
|
|
* table. */
|
|
Array16Of<MathValueRecord> 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<MathValueRecord>
|
|
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> 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<hb_codepoint_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<Coverage>
|
|
mathKernCoverage;
|
|
/* Offset to Coverage table -
|
|
* from the beginning of the
|
|
* MathKernInfo table. */
|
|
Array16Of<MathKernInfoRecord>
|
|
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> mathItalicsCorrectionInfo;
|
|
|
|
/* Offset to MathTopAccentAttachment table -
|
|
* from the beginning of MathGlyphInfo table. */
|
|
Offset16To<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.. */
|
|
Offset16To<Coverage> extendedShapeCoverage;
|
|
|
|
/* Offset to MathKernInfo table -
|
|
* from the beginning of MathGlyphInfo table. */
|
|
Offset16To<MathKernInfo> 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<HBUINT16> (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<MathGlyphPartRecord>
|
|
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<MathGlyphAssembly> glyphAssembly;
|
|
|
|
/* MathGlyphVariantRecords for alternative variants of the glyphs. */
|
|
Array16Of<MathGlyphVariantRecord> 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<const Offset16To<MathGlyphConstruction>> 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<hb_codepoint_t>& new_coverage,
|
|
const Offset16To<Coverage>& 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<hb_codepoint_t> new_vert_coverage;
|
|
hb_sorted_vector_t<hb_codepoint_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> &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<Coverage> vertGlyphCoverage;
|
|
/* Offset to Coverage table -
|
|
* from the beginning of MathVariants
|
|
* table. */
|
|
Offset16To<Coverage> 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<Offset16To<MathGlyphConstruction>>
|
|
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; /* MathConstants table */
|
|
Offset16To<MathGlyphInfo>
|
|
mathGlyphInfo; /* MathGlyphInfo table */
|
|
Offset16To<MathVariants>
|
|
mathVariants; /* MathVariants table */
|
|
|
|
public:
|
|
DEFINE_SIZE_STATIC (10);
|
|
};
|
|
|
|
} /* namespace OT */
|
|
|
|
|
|
#endif /* HB_OT_MATH_TABLE_HH */
|