Merge branch 'math'

Supercedes https://github.com/behdad/harfbuzz/pull/326
Fixes https://github.com/behdad/harfbuzz/pull/331
This commit is contained in:
Behdad Esfahbod 2016-09-27 17:15:59 +02:00
commit 6d3e6abf0c
18 changed files with 1944 additions and 6 deletions

View File

@ -80,6 +80,7 @@
<xi:include href="xml/hb-ot-tag.xml"/>
<xi:include href="xml/hb-ot-font.xml"/>
<xi:include href="xml/hb-ot-shape.xml"/>
<xi:include href="xml/hb-ot-math.xml"/>
<xi:include href="xml/hb-shape-plan.xml"/>
@ -175,6 +176,14 @@
<title>Index of new symbols in 1.1.3</title>
<xi:include href="xml/api-index-1.1.3.xml"><xi:fallback /></xi:include>
</index>
<index id="api-index-1-2-3" role="1.2.3">
<title>Index of new symbols in 1.2.3</title>
<xi:include href="xml/api-index-1.2.3.xml"><xi:fallback /></xi:include>
</index>
<index id="api-index-1-4-0" role="1.4.0">
<title>Index of new symbols in 1.4.0</title>
<xi:include href="xml/api-index-1.4.0.xml"><xi:fallback /></xi:include>
</index>
<index id="deprecated-api-index" role="deprecated">
<title>Index of deprecated API</title>
<xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>

View File

@ -144,6 +144,8 @@ uint8_t
HB_BUFFER_FLAGS_DEFAULT
HB_BUFFER_SERIALIZE_FLAGS_DEFAULT
HB_SCRIPT_CANADIAN_ABORIGINAL
hb_font_funcs_set_glyph_func
hb_font_get_glyph_func_t
</SECTION>
<SECTION>
@ -193,7 +195,6 @@ hb_font_funcs_reference
hb_font_funcs_set_glyph_contour_point_func
hb_font_funcs_set_glyph_extents_func
hb_font_funcs_set_glyph_from_name_func
hb_font_funcs_set_glyph_func
hb_font_funcs_set_glyph_h_advance_func
hb_font_funcs_set_glyph_h_kerning_func
hb_font_funcs_set_glyph_h_origin_func
@ -201,7 +202,9 @@ hb_font_funcs_set_glyph_name_func
hb_font_funcs_set_glyph_v_advance_func
hb_font_funcs_set_glyph_v_kerning_func
hb_font_funcs_set_glyph_v_origin_func
hb_font_funcs_set_nominal_glyph_func
hb_font_funcs_set_user_data
hb_font_funcs_set_variation_glyph_func
hb_font_funcs_t
hb_font_get_empty
hb_font_get_face
@ -216,7 +219,6 @@ hb_font_get_glyph_extents_for_origin
hb_font_get_glyph_extents_func_t
hb_font_get_glyph_from_name
hb_font_get_glyph_from_name_func_t
hb_font_get_glyph_func_t
hb_font_get_glyph_h_advance
hb_font_get_glyph_h_advance_func_t
hb_font_get_glyph_h_kerning
@ -235,10 +237,14 @@ hb_font_get_glyph_v_kerning
hb_font_get_glyph_v_kerning_func_t
hb_font_get_glyph_v_origin
hb_font_get_glyph_v_origin_func_t
hb_font_get_nominal_glyph
hb_font_get_nominal_glyph_func_t
hb_font_get_parent
hb_font_get_ppem
hb_font_get_scale
hb_font_get_user_data
hb_font_get_variation_glyph
hb_font_get_variation_glyph_func_t
hb_font_glyph_from_string
hb_font_glyph_to_string
hb_font_is_immutable
@ -416,6 +422,26 @@ Xhb_ot_layout_lookup_position
Xhb_ot_layout_lookup_substitute
</SECTION>
<SECTION>
<FILE>hb-ot-math</FILE>
HB_OT_TAG_MATH
HB_OT_MATH_SCRIPT
hb_ot_math_constant_t
hb_ot_math_glyph_part_flags_t
hb_ot_math_glyph_part_t
hb_ot_math_glyph_variant_t
hb_ot_math_get_constant
hb_ot_math_get_glyph_assembly
hb_ot_math_get_glyph_italics_correction
hb_ot_math_get_glyph_kerning
hb_ot_math_get_glyph_top_accent_attachment
hb_ot_math_get_glyph_variants
hb_ot_math_get_min_connector_overlap
hb_ot_math_has_data
hb_ot_math_is_glyph_extended_shape
hb_ot_math_kern_t
</SECTION>
<SECTION>
<FILE>hb-ot-tag</FILE>
HB_OT_TAG_DEFAULT_LANGUAGE

View File

@ -75,6 +75,7 @@ HB_OT_sources = \
hb-ot-layout-gsubgpos-private.hh \
hb-ot-layout-gsub-table.hh \
hb-ot-layout-jstf-table.hh \
hb-ot-layout-math-table.hh \
hb-ot-layout-private.hh \
hb-ot-map.cc \
hb-ot-map-private.hh \
@ -111,6 +112,7 @@ HB_OT_headers = \
hb-ot.h \
hb-ot-font.h \
hb-ot-layout.h \
hb-ot-math.h \
hb-ot-shape.h \
hb-ot-tag.h \
$(NULL)

View File

@ -116,8 +116,12 @@ struct hb_font_t {
/* Convert from font-space to user-space */
inline hb_position_t em_scale_x (int16_t v) { return em_scale (v, this->x_scale); }
inline hb_position_t em_scale_y (int16_t v) { return em_scale (v, this->y_scale); }
inline int dir_scale (hb_direction_t direction)
{ return HB_DIRECTION_IS_VERTICAL(direction) ? y_scale : x_scale; }
inline hb_position_t em_scale_x (int16_t v) { return em_scale (v, x_scale); }
inline hb_position_t em_scale_y (int16_t v) { return em_scale (v, y_scale); }
inline hb_position_t em_scale_dir (int16_t v, hb_direction_t direction)
{ return em_scale (v, dir_scale (direction)); }
/* Convert from parent-font user-space to our user-space */
inline hb_position_t parent_scale_x_distance (hb_position_t v) {
@ -504,7 +508,6 @@ struct hb_font_t {
return false;
}
private:
inline hb_position_t em_scale (int16_t v, int scale)
{
int upem = face->get_upem ();

View File

@ -0,0 +1,722 @@
/*
* 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_LAYOUT_MATH_TABLE_HH
#define HB_OT_LAYOUT_MATH_TABLE_HH
#include "hb-open-type-private.hh"
#include "hb-ot-layout-common-private.hh"
#include "hb-ot-math.h"
namespace OT {
struct MathValueRecord
{
inline 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); }
inline 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); }
inline 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:
SHORT value; /* The X or Y value in design units */
OffsetTo<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
{
inline 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);
}
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) && sanitize_math_value_records(c));
}
inline 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:
SHORT percentScaleDown[2];
USHORT minHeight[2];
MathValueRecord mathValueRecords[51];
SHORT radicalDegreeBottomRaisePercent;
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 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:
OffsetTo<Coverage> coverage; /* Offset to Coverage table -
* from the beginning of
* MathItalicsCorrectionInfo
* table. */
ArrayOf<MathValueRecord> italicsCorrection; /* Array of MathValueRecords
* defining italics correction
* values for each
* covered glyph. */
public:
DEFINE_SIZE_ARRAY (4, 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 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:
OffsetTo<Coverage> topAccentCoverage; /* Offset to Coverage table -
* from the beginning of
* MathTopAccentAttachment
* table. */
ArrayOf<MathValueRecord> topAccentAttachment; /* Array of MathValueRecords
* defining top accent
* attachment points for each
* covered glyph. */
public:
DEFINE_SIZE_ARRAY (2 + 2, topAccentAttachment);
};
struct MathKern
{
inline bool sanitize_math_value_records (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
unsigned int count = 2 * heightCount + 1;
for (unsigned int i = 0; i < count; i++)
if (!mathValueRecords[i].sanitize (c, this)) return_trace (false);
return_trace (true);
}
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) &&
c->check_array (mathValueRecords,
mathValueRecords[0].static_size,
2 * heightCount + 1) &&
sanitize_math_value_records (c));
}
inline hb_position_t get_value (hb_position_t correction_height, hb_font_t *font) const
{
const MathValueRecord* correctionHeight = mathValueRecords;
const MathValueRecord* kernValue = mathValueRecords + 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);
}
protected:
USHORT heightCount;
MathValueRecord mathValueRecords[VAR]; /* 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, mathValueRecords);
};
struct MathKernInfoRecord
{
inline 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);
}
inline 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);
}
protected:
/* Offset to MathKern table for each corner -
* from the beginning of MathKernInfo table. May be NULL. */
OffsetTo<MathKern> mathKern[4];
public:
DEFINE_SIZE_STATIC (8);
};
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 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);
}
protected:
OffsetTo<Coverage> mathKernCoverage; /* Offset to Coverage table -
* from the beginning of the
* MathKernInfo table. */
ArrayOf<MathKernInfoRecord> mathKernInfoRecords; /* Array of
* MathKernInfoRecords,
* per-glyph information for
* mathematical positioning
* of subscripts and
* superscripts. */
public:
DEFINE_SIZE_ARRAY (4, 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 hb_position_t
get_italics_correction (hb_codepoint_t glyph, hb_font_t *font) const
{ return (this+mathItalicsCorrectionInfo).get_value (glyph, font); }
inline hb_position_t
get_top_accent_attachment (hb_codepoint_t glyph, hb_font_t *font) const
{ return (this+mathTopAccentAttachment).get_value (glyph, font); }
inline bool is_extended_shape (hb_codepoint_t glyph) const
{ return (this+extendedShapeCoverage).get_coverage (glyph) != NOT_COVERED; }
inline 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); }
protected:
/* Offset to MathItalicsCorrectionInfo table -
* from the beginning of MathGlyphInfo table. */
OffsetTo<MathItalicsCorrectionInfo> mathItalicsCorrectionInfo;
/* Offset to MathTopAccentAttachment table -
* from the beginning of MathGlyphInfo table. */
OffsetTo<MathTopAccentAttachment> mathTopAccentAttachment;
/* Offset to coverage table for Extended Shape glyphs -
* from the beginning of MathGlyphInfo table. When the left or right glyph of
* a box is an extended shape variant, the (ink) box (and not the default
* position defined by values in MathConstants table) should be used for
* vertical positioning purposes. May be NULL.. */
OffsetTo<Coverage> extendedShapeCoverage;
/* Offset to MathKernInfo table -
* from the beginning of MathGlyphInfo table. */
OffsetTo<MathKernInfo> mathKernInfo;
public:
DEFINE_SIZE_STATIC (8);
};
struct MathGlyphVariantRecord
{
friend struct MathGlyphConstruction;
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this));
}
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 (4);
};
struct PartFlags : USHORT
{
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
{
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this));
}
inline void extract (hb_ot_math_glyph_part_t &out,
int scale,
hb_font_t *font) const
{
out.glyph = glyph;
out.start_connector_length = font->em_scale (startConnectorLength, scale);
out.end_connector_length = font->em_scale (endConnectorLength, scale);
out.full_advance = font->em_scale (fullAdvance, scale);
ASSERT_STATIC ((unsigned int) HB_MATH_GLYPH_PART_FLAG_EXTENDER ==
(unsigned int) PartFlags::Extender);
out.flags = (hb_ot_math_glyph_part_flags_t)
(unsigned int)
(partFlags & PartFlags::Defined);
}
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 (10);
};
struct MathGlyphAssembly
{
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 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)
{
int scale = font->dir_scale (direction);
const MathGlyphPartRecord *arr =
partRecords.sub_array (start_offset, parts_count);
unsigned int count = *parts_count;
for (unsigned int i = 0; i < count; i++)
arr[i].extract (parts[i], scale, font);
}
if (italics_correction)
*italics_correction = italicsCorrection.get_x_value (font, this);
return partRecords.len;
}
protected:
MathValueRecord italicsCorrection; /* Italics correction of this
* MathGlyphAssembly. Should not
* depend on the assembly size. */
ArrayOf<MathGlyphPartRecord> partRecords; /* Array of part records, from
* left to right and bottom to
* top. */
public:
DEFINE_SIZE_ARRAY (6, 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 const MathGlyphAssembly &get_assembly (void) const
{ return this+glyphAssembly; }
inline 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)
{
int scale = font->dir_scale (direction);
const MathGlyphVariantRecord *arr =
mathGlyphVariantRecord.sub_array (start_offset, variants_count);
unsigned int count = *variants_count;
for (unsigned int i = 0; i < count; i++)
{
variants[i].glyph = arr[i].variantGlyph;
variants[i].advance = font->em_scale (arr[i].advanceMeasurement, scale);
}
}
return mathGlyphVariantRecord.len;
}
protected:
/* Offset to MathGlyphAssembly table for this shape - from the beginning of
MathGlyphConstruction table. May be NULL. */
OffsetTo<MathGlyphAssembly> glyphAssembly;
/* MathGlyphVariantRecords for alternative variants of the glyphs. */
ArrayOf<MathGlyphVariantRecord> mathGlyphVariantRecord;
public:
DEFINE_SIZE_ARRAY (4, 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_direction_t direction,
hb_font_t *font) const
{ return font->em_scale_dir (minConnectorOverlap, direction); }
inline 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); }
inline 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:
inline const MathGlyphConstruction &
get_glyph_construction (hb_codepoint_t glyph,
hb_direction_t direction,
hb_font_t *font) const
{
bool vertical = HB_DIRECTION_IS_VERTICAL (direction);
unsigned int count = vertical ? vertGlyphCount : horizGlyphCount;
const OffsetTo<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:
USHORT minConnectorOverlap; /* Minimum overlap of connecting
* glyphs during glyph construction,
* in design units. */
OffsetTo<Coverage> vertGlyphCoverage; /* Offset to Coverage table -
* from the beginning of MathVariants
* table. */
OffsetTo<Coverage> 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<MathGlyphConstruction> glyphConstruction[VAR];
public:
DEFINE_SIZE_ARRAY (10, glyphConstruction);
};
/*
* MATH -- The MATH Table
*/
struct MATH
{
static const hb_tag_t tableTag = HB_OT_TAG_MATH;
inline 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));
}
inline hb_position_t get_constant (hb_ot_math_constant_t constant,
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 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;/* MathConstants table */
OffsetTo<MathGlyphInfo> mathGlyphInfo;/* MathGlyphInfo table */
OffsetTo<MathVariants> mathVariants; /* MathVariants table */
public:
DEFINE_SIZE_STATIC (10);
};
} /* mathspace OT */
#endif /* HB_OT_LAYOUT_MATH_TABLE_HH */

View File

@ -124,6 +124,7 @@ namespace OT {
struct GDEF;
struct GSUB;
struct GPOS;
struct MATH;
}
struct hb_ot_layout_lookup_accelerator_t
@ -152,10 +153,12 @@ struct hb_ot_layout_t
hb_blob_t *gdef_blob;
hb_blob_t *gsub_blob;
hb_blob_t *gpos_blob;
hb_blob_t *math_blob;
const struct OT::GDEF *gdef;
const struct OT::GSUB *gsub;
const struct OT::GPOS *gpos;
const struct OT::MATH *math;
unsigned int gsub_lookup_count;
unsigned int gpos_lookup_count;

View File

@ -35,6 +35,7 @@
#include "hb-ot-layout-gsub-table.hh"
#include "hb-ot-layout-gpos-table.hh"
#include "hb-ot-layout-jstf-table.hh"
#include "hb-ot-layout-math-table.hh"
#include "hb-ot-map-private.hh"
@ -60,6 +61,10 @@ _hb_ot_layout_create (hb_face_t *face)
layout->gpos_blob = OT::Sanitizer<OT::GPOS>::sanitize (face->reference_table (HB_OT_TAG_GPOS));
layout->gpos = OT::Sanitizer<OT::GPOS>::lock_instance (layout->gpos_blob);
/* The MATH table is rarely used, so only try and load it in _get_math. */
layout->math_blob = NULL;
layout->math = NULL;
{
/*
* The ugly business of blacklisting individual fonts' tables happen here!
@ -177,6 +182,7 @@ _hb_ot_layout_destroy (hb_ot_layout_t *layout)
hb_blob_destroy (layout->gdef_blob);
hb_blob_destroy (layout->gsub_blob);
hb_blob_destroy (layout->gpos_blob);
hb_blob_destroy (layout->math_blob);
free (layout);
}
@ -199,6 +205,30 @@ _get_gpos (hb_face_t *face)
if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GPOS);
return *hb_ot_layout_from_face (face)->gpos;
}
static inline const OT::MATH&
_get_math (hb_face_t *face)
{
if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::MATH);
hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
retry:
const OT::MATH *math = (const OT::MATH *) hb_atomic_ptr_get (&layout->math);
if (unlikely (!math))
{
hb_blob_t *blob = OT::Sanitizer<OT::MATH>::sanitize (face->reference_table (HB_OT_TAG_MATH));
math = OT::Sanitizer<OT::MATH>::lock_instance (blob);
if (!hb_atomic_ptr_cmpexch (&layout->math, NULL, math))
{
hb_blob_destroy (blob);
goto retry;
}
layout->math_blob = blob;
}
return *math;
}
/*
@ -1190,3 +1220,230 @@ hb_ot_layout_substitute_lookup (OT::hb_apply_context_t *c,
{
apply_string<GSUBProxy> (c, lookup, accel);
}
/*
* MATH
*/
/* TODO Move this to hb-ot-math.cc and separate it from hb_ot_layout_t. */
/**
* hb_ot_math_has_data:
*
* @face: #hb_face_t to test
*
* This function allows to verify the presence of an OpenType MATH table on the
* face. If so, such a table will be loaded into memory and sanitized. You can
* then safely call other functions for math layout and shaping.
*
* Return value: #TRUE if face has a MATH table and #FALSE otherwise
*
* Since: 1.4.0
**/
hb_bool_t
hb_ot_math_has_data (hb_face_t *face)
{
return &_get_math (face) != &OT::Null(OT::MATH);
}
/**
* hb_ot_math_get_constant:
*
* @font: #hb_font_t from which to retrieve the value
* @constant: #hb_ot_math_constant_t the constant to retrieve
*
* This function returns the requested math constants as a #hb_position_t.
* If the request constant is HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN,
* HB_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN or
* HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN then the return value is
* actually an integer between 0 and 100 representing that percentage.
*
* Return value: the requested constant or 0
*
* Since: 1.4.0
**/
hb_position_t
hb_ot_math_get_constant (hb_font_t *font,
hb_ot_math_constant_t constant)
{
const OT::MATH &math = _get_math (font->face);
return math.get_constant(constant, font);
}
/**
* hb_ot_math_get_glyph_italics_correction:
*
* @font: #hb_font_t from which to retrieve the value
* @glyph: glyph index from which to retrieve the value
*
* Return value: the italics correction of the glyph or 0
*
* Since: 1.4.0
**/
hb_position_t
hb_ot_math_get_glyph_italics_correction (hb_font_t *font,
hb_codepoint_t glyph)
{
const OT::MATH &math = _get_math (font->face);
return math.get_math_glyph_info().get_italics_correction (glyph, font);
}
/**
* hb_ot_math_get_glyph_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: 1.4.0
**/
hb_position_t
hb_ot_math_get_glyph_top_accent_attachment (hb_font_t *font,
hb_codepoint_t glyph)
{
const OT::MATH &math = _get_math (font->face);
return math.get_math_glyph_info().get_top_accent_attachment (glyph, font);
}
/**
* hb_ot_math_is_glyph_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: 1.4.0
**/
hb_bool_t
hb_ot_math_is_glyph_extended_shape (hb_face_t *face,
hb_codepoint_t glyph)
{
const OT::MATH &math = _get_math (face);
return math.get_math_glyph_info().is_extended_shape (glyph);
}
/**
* hb_ot_math_get_glyph_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: 1.4.0
**/
hb_position_t
hb_ot_math_get_glyph_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);
return math.get_math_glyph_info().get_kerning (glyph, kern, correction_height, font);
}
/**
* hb_ot_math_get_glyph_variants:
*
* @font: #hb_font_t from which to retrieve the values
* @glyph: index of the glyph to stretch
* @direction: direction of the stretching
* @start_offset: offset of the first variant to retrieve
* @variants_count: maximum number of variants to retrieve after start_offset
* (IN) and actual number of variants retrieved (OUT)
* @variants: array of size at least @variants_count to store the result
*
* This function tries to retrieve the MathGlyphConstruction for the specified
* font, glyph and direction. Note that only the value of
* #HB_DIRECTION_IS_HORIZONTAL is considered. It provides the corresponding list
* of size variants as an array of hb_ot_math_glyph_variant_t structs.
*
* Return value: the total number of size variants available or 0
*
* Since: 1.4.0
**/
unsigned int
hb_ot_math_get_glyph_variants (hb_font_t *font,
hb_codepoint_t glyph,
hb_direction_t direction,
unsigned int start_offset,
unsigned int *variants_count, /* IN/OUT */
hb_ot_math_glyph_variant_t *variants /* OUT */)
{
const OT::MATH &math = _get_math (font->face);
return math.get_math_variants().get_glyph_variants (glyph, direction, font,
start_offset,
variants_count,
variants);
}
/**
* hb_ot_math_get_min_connector_overlap:
*
* @font: #hb_font_t from which to retrieve the value
* @direction: direction of the stretching
*
* This function tries to retrieve the MathVariants table for the specified
* font and returns the minimum overlap of connecting glyphs to draw a glyph
* assembly in the specified direction. Note that only the value of
* #HB_DIRECTION_IS_HORIZONTAL is considered.
*
* Return value: requested min connector overlap or 0
*
* Since: 1.4.0
**/
hb_position_t
hb_ot_math_get_min_connector_overlap (hb_font_t *font,
hb_direction_t direction)
{
const OT::MATH &math = _get_math (font->face);
return math.get_math_variants().get_min_connector_overlap (direction, font);
}
/**
* hb_ot_math_get_glyph_assembly:
*
* @font: #hb_font_t from which to retrieve the values
* @glyph: index of the glyph to stretch
* @direction: direction of the stretching
* @start_offset: offset of the first glyph part to retrieve
* @parts_count: maximum number of glyph parts to retrieve after start_offset
* (IN) and actual number of parts retrieved (OUT)
* @parts: array of size at least @parts_count to store the result
* @italics_correction: italic correction of the glyph assembly
*
* This function tries to retrieve the GlyphAssembly for the specified font,
* glyph and direction. Note that only the value of #HB_DIRECTION_IS_HORIZONTAL
* is considered. It provides the information necessary to draw the glyph
* assembly as an array of #hb_ot_math_glyph_part_t.
*
* Return value: the total number of parts in the glyph assembly
*
* Since: 1.4.0
**/
unsigned int
hb_ot_math_get_glyph_assembly (hb_font_t *font,
hb_codepoint_t glyph,
hb_direction_t direction,
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 OT::MATH &math = _get_math (font->face);
return math.get_math_variants().get_glyph_parts (glyph, direction, font,
start_offset,
parts_count,
parts,
italics_correction);
}

209
src/hb-ot-math.h Normal file
View File

@ -0,0 +1,209 @@
/*
* 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_H_IN
#error "Include <hb-ot.h> instead."
#endif
#ifndef HB_OT_MATH_H
#define HB_OT_MATH_H
#include "hb.h"
HB_BEGIN_DECLS
/*
* MATH
*/
#define HB_OT_TAG_MATH HB_TAG('M','A','T','H')
/* Use with hb_buffer_set_script() for math shaping. */
#define HB_OT_MATH_SCRIPT HB_TAG('m','a','t','h')
/* Types */
/**
* hb_ot_math_constant_t:
*
* Since: 1.4.0
*/
typedef enum {
HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN = 0,
HB_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN = 1,
HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT = 2,
HB_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT = 3,
HB_OT_MATH_CONSTANT_MATH_LEADING = 4,
HB_OT_MATH_CONSTANT_AXIS_HEIGHT = 5,
HB_OT_MATH_CONSTANT_ACCENT_BASE_HEIGHT = 6,
HB_OT_MATH_CONSTANT_FLATTENED_ACCENT_BASE_HEIGHT = 7,
HB_OT_MATH_CONSTANT_SUBSCRIPT_SHIFT_DOWN = 8,
HB_OT_MATH_CONSTANT_SUBSCRIPT_TOP_MAX = 9,
HB_OT_MATH_CONSTANT_SUBSCRIPT_BASELINE_DROP_MIN = 10,
HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP = 11,
HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP_CRAMPED = 12,
HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MIN = 13,
HB_OT_MATH_CONSTANT_SUPERSCRIPT_BASELINE_DROP_MAX = 14,
HB_OT_MATH_CONSTANT_SUB_SUPERSCRIPT_GAP_MIN = 15,
HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MAX_WITH_SUBSCRIPT = 16,
HB_OT_MATH_CONSTANT_SPACE_AFTER_SCRIPT = 17,
HB_OT_MATH_CONSTANT_UPPER_LIMIT_GAP_MIN = 18,
HB_OT_MATH_CONSTANT_UPPER_LIMIT_BASELINE_RISE_MIN = 19,
HB_OT_MATH_CONSTANT_LOWER_LIMIT_GAP_MIN = 20,
HB_OT_MATH_CONSTANT_LOWER_LIMIT_BASELINE_DROP_MIN = 21,
HB_OT_MATH_CONSTANT_STACK_TOP_SHIFT_UP = 22,
HB_OT_MATH_CONSTANT_STACK_TOP_DISPLAY_STYLE_SHIFT_UP = 23,
HB_OT_MATH_CONSTANT_STACK_BOTTOM_SHIFT_DOWN = 24,
HB_OT_MATH_CONSTANT_STACK_BOTTOM_DISPLAY_STYLE_SHIFT_DOWN = 25,
HB_OT_MATH_CONSTANT_STACK_GAP_MIN = 26,
HB_OT_MATH_CONSTANT_STACK_DISPLAY_STYLE_GAP_MIN = 27,
HB_OT_MATH_CONSTANT_STRETCH_STACK_TOP_SHIFT_UP = 28,
HB_OT_MATH_CONSTANT_STRETCH_STACK_BOTTOM_SHIFT_DOWN = 29,
HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_ABOVE_MIN = 30,
HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_BELOW_MIN = 31,
HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_SHIFT_UP = 32,
HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_DISPLAY_STYLE_SHIFT_UP = 33,
HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_SHIFT_DOWN = 34,
HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_DISPLAY_STYLE_SHIFT_DOWN = 35,
HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_GAP_MIN = 36,
HB_OT_MATH_CONSTANT_FRACTION_NUM_DISPLAY_STYLE_GAP_MIN = 37,
HB_OT_MATH_CONSTANT_FRACTION_RULE_THICKNESS = 38,
HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_GAP_MIN = 39,
HB_OT_MATH_CONSTANT_FRACTION_DENOM_DISPLAY_STYLE_GAP_MIN = 40,
HB_OT_MATH_CONSTANT_SKEWED_FRACTION_HORIZONTAL_GAP = 41,
HB_OT_MATH_CONSTANT_SKEWED_FRACTION_VERTICAL_GAP = 42,
HB_OT_MATH_CONSTANT_OVERBAR_VERTICAL_GAP = 43,
HB_OT_MATH_CONSTANT_OVERBAR_RULE_THICKNESS = 44,
HB_OT_MATH_CONSTANT_OVERBAR_EXTRA_ASCENDER = 45,
HB_OT_MATH_CONSTANT_UNDERBAR_VERTICAL_GAP = 46,
HB_OT_MATH_CONSTANT_UNDERBAR_RULE_THICKNESS = 47,
HB_OT_MATH_CONSTANT_UNDERBAR_EXTRA_DESCENDER = 48,
HB_OT_MATH_CONSTANT_RADICAL_VERTICAL_GAP = 49,
HB_OT_MATH_CONSTANT_RADICAL_DISPLAY_STYLE_VERTICAL_GAP = 50,
HB_OT_MATH_CONSTANT_RADICAL_RULE_THICKNESS = 51,
HB_OT_MATH_CONSTANT_RADICAL_EXTRA_ASCENDER = 52,
HB_OT_MATH_CONSTANT_RADICAL_KERN_BEFORE_DEGREE = 53,
HB_OT_MATH_CONSTANT_RADICAL_KERN_AFTER_DEGREE = 54,
HB_OT_MATH_CONSTANT_RADICAL_DEGREE_BOTTOM_RAISE_PERCENT = 55
} hb_ot_math_constant_t;
/**
* hb_ot_math_kern_t:
*
* Since: 1.4.0
*/
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_ot_math_glyph_variant_t:
*
* Since: 1.4.0
*/
typedef struct hb_ot_math_glyph_variant_t {
hb_codepoint_t glyph;
hb_position_t advance;
} hb_ot_math_glyph_variant_t;
/**
* hb_ot_math_glyph_part_flags_t:
*
* Since: 1.4.0
*/
typedef enum { /*< flags >*/
HB_MATH_GLYPH_PART_FLAG_EXTENDER = 0x00000001u /* Extender glyph */
} hb_ot_math_glyph_part_flags_t;
/**
* hb_ot_math_glyph_part_t:
*
* Since: 1.4.0
*/
typedef struct hb_ot_math_glyph_part_t {
hb_codepoint_t glyph;
hb_position_t start_connector_length;
hb_position_t end_connector_length;
hb_position_t full_advance;
hb_ot_math_glyph_part_flags_t flags;
} hb_ot_math_glyph_part_t;
/* Methods */
HB_EXTERN hb_bool_t
hb_ot_math_has_data (hb_face_t *face);
HB_EXTERN hb_position_t
hb_ot_math_get_constant (hb_font_t *font,
hb_ot_math_constant_t constant);
HB_EXTERN hb_position_t
hb_ot_math_get_glyph_italics_correction (hb_font_t *font,
hb_codepoint_t glyph);
HB_EXTERN hb_position_t
hb_ot_math_get_glyph_top_accent_attachment (hb_font_t *font,
hb_codepoint_t glyph);
HB_EXTERN hb_bool_t
hb_ot_math_is_glyph_extended_shape (hb_face_t *face,
hb_codepoint_t glyph);
HB_EXTERN hb_position_t
hb_ot_math_get_glyph_kerning (hb_font_t *font,
hb_codepoint_t glyph,
hb_ot_math_kern_t kern,
hb_position_t correction_height);
HB_EXTERN unsigned int
hb_ot_math_get_glyph_variants (hb_font_t *font,
hb_codepoint_t glyph,
hb_direction_t direction,
unsigned int start_offset,
unsigned int *variants_count, /* IN/OUT */
hb_ot_math_glyph_variant_t *variants /* OUT */);
HB_EXTERN hb_position_t
hb_ot_math_get_min_connector_overlap (hb_font_t *font,
hb_direction_t direction);
HB_EXTERN unsigned int
hb_ot_math_get_glyph_assembly (hb_font_t *font,
hb_codepoint_t glyph,
hb_direction_t direction,
unsigned int start_offset,
unsigned int *parts_count, /* IN/OUT */
hb_ot_math_glyph_part_t *parts, /* OUT */
hb_position_t *italics_correction /* OUT */);
HB_END_DECLS
#endif /* HB_OT_MATH_H */

View File

@ -32,6 +32,7 @@
#include "hb-ot-font.h"
#include "hb-ot-layout.h"
#include "hb-ot-math.h"
#include "hb-ot-tag.h"
#include "hb-ot-shape.h"

View File

@ -43,10 +43,20 @@ endif
if HAVE_OT
TEST_PROGS += \
test-ot-tag \
$(NULL)
endif
if HAVE_FREETYPE
TEST_PROGS += \
test-ot-math \
$(NULL)
test_ot_math_LDADD = $(LDADD) $(FREETYPE_LIBS)
test_ot_math_CPPFLAGS = $(AM_CPPFLAGS) $(FREETYPE_CFLAGS)
endif # HAVE_FREETYPE
endif # HAVE_OT
# Tests for header compilation
TEST_PROGS += \
@ -73,6 +83,8 @@ TESTS_ENVIRONMENT = \
G_DEBUG=gc-friendly \
G_SLICE=always-malloc \
srcdir=$(srcdir) \
G_TEST_SRCDIR=$(abs_srcdir) \
G_TEST_BUILDDIR=$(abs_builddir) \
$(NULL)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

694
test/api/test-ot-math.c Normal file
View File

@ -0,0 +1,694 @@
/*
* 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
*/
#include "hb-test.h"
#include "hb-ft.h"
#include "hb-ot.h"
/* Unit tests for hb-ot-math.h - OpenType MATH table */
static FT_Library ft_library;
static FT_Face ft_face;
static hb_font_t *hb_font;
static hb_face_t *hb_face;
static inline void
initFreeType (void)
{
FT_Error ft_error;
if ((ft_error = FT_Init_FreeType (&ft_library)))
abort();
}
static inline void
cleanupFreeType (void)
{
FT_Done_FreeType (ft_library);
}
static void
openFont(const char* fontFile)
{
gchar* path = g_test_build_filename(G_TEST_DIST, fontFile, NULL);
FT_Error ft_error;
if ((ft_error = FT_New_Face (ft_library, path, 0, &ft_face))) {
g_free(path);
abort();
}
g_free(path);
if ((ft_error = FT_Set_Char_Size (ft_face, 2000, 1000, 0, 0)))
abort();
hb_font = hb_ft_font_create (ft_face, NULL);
hb_face = hb_ft_face_create_cached(ft_face);
}
static inline void
closeFont (void)
{
hb_font_destroy (hb_font);
FT_Done_Face (ft_face);
}
static void
test_has_data (void)
{
initFreeType();
openFont("fonts/MathTestFontNone.otf");
g_assert(!hb_ot_math_has_data (hb_face)); // MATH table not available
closeFont();
openFont("fonts/MathTestFontEmpty.otf");
g_assert(hb_ot_math_has_data (hb_face)); // MATH table available
closeFont();
cleanupFreeType();
}
static void
test_get_constant (void)
{
initFreeType();
openFont("fonts/MathTestFontEmpty.otf");
g_assert_cmpint(hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT), ==, 0); // MathConstants not available
closeFont();
openFont("fonts/MathTestFontFull.otf");
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT)), ==, 100);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT)), ==, 200);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_MATH_LEADING)), ==, 300);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_AXIS_HEIGHT)), ==, 400);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_ACCENT_BASE_HEIGHT)), ==, 500);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_FLATTENED_ACCENT_BASE_HEIGHT)), ==, 600);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_SUBSCRIPT_SHIFT_DOWN)), ==, 700);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_SUBSCRIPT_TOP_MAX)), ==, 800);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_SUBSCRIPT_BASELINE_DROP_MIN)), ==, 900);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP)), ==, 1100);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP_CRAMPED)), ==, 1200);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MIN)), ==, 1300);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_SUPERSCRIPT_BASELINE_DROP_MAX)), ==, 1400);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_SUB_SUPERSCRIPT_GAP_MIN)), ==, 1500);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MAX_WITH_SUBSCRIPT)), ==, 1600);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_SPACE_AFTER_SCRIPT)), ==, 3400);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_UPPER_LIMIT_GAP_MIN)), ==, 1800);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_UPPER_LIMIT_BASELINE_RISE_MIN)), ==, 1900);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_LOWER_LIMIT_GAP_MIN)), ==, 2200);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_LOWER_LIMIT_BASELINE_DROP_MIN)), ==, 2300);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_STACK_TOP_SHIFT_UP)), ==, 2400);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_STACK_TOP_DISPLAY_STYLE_SHIFT_UP)), ==, 2500);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_STACK_BOTTOM_SHIFT_DOWN)), ==, 2600);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_STACK_BOTTOM_DISPLAY_STYLE_SHIFT_DOWN)), ==, 2700);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_STACK_GAP_MIN)), ==, 2800);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_STACK_DISPLAY_STYLE_GAP_MIN)), ==, 2900);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_STRETCH_STACK_TOP_SHIFT_UP)), ==, 3000);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_STRETCH_STACK_BOTTOM_SHIFT_DOWN)), ==, 3100);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_ABOVE_MIN)), ==, 3200);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_BELOW_MIN)), ==, 3300);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_SHIFT_UP)), ==, 3400);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_DISPLAY_STYLE_SHIFT_UP)), ==, 3500);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_SHIFT_DOWN)), ==, 3600);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_DISPLAY_STYLE_SHIFT_DOWN)), ==, 3700);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_GAP_MIN)), ==, 3800);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_FRACTION_NUM_DISPLAY_STYLE_GAP_MIN)), ==, 3900);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_FRACTION_RULE_THICKNESS)), ==, 4000);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_GAP_MIN)), ==, 4100);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_FRACTION_DENOM_DISPLAY_STYLE_GAP_MIN)), ==, 4200);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_SKEWED_FRACTION_HORIZONTAL_GAP)), ==, 8600);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_SKEWED_FRACTION_VERTICAL_GAP)), ==, 4400);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_OVERBAR_VERTICAL_GAP)), ==, 4500);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_OVERBAR_RULE_THICKNESS)), ==, 4600);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_OVERBAR_EXTRA_ASCENDER)), ==, 4700);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_UNDERBAR_VERTICAL_GAP)), ==, 4800);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_UNDERBAR_RULE_THICKNESS)), ==, 4900);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_UNDERBAR_EXTRA_DESCENDER)), ==, 5000);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_RADICAL_VERTICAL_GAP)), ==, 5100);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_RADICAL_DISPLAY_STYLE_VERTICAL_GAP)), ==, 5200);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_RADICAL_RULE_THICKNESS)), ==, 5300);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_RADICAL_EXTRA_ASCENDER)), ==, 5400);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_RADICAL_KERN_BEFORE_DEGREE)), ==, 11000);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_RADICAL_KERN_AFTER_DEGREE)), ==, 11200);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN)), ==, 87);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN)), ==, 76);
g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_RADICAL_DEGREE_BOTTOM_RAISE_PERCENT)), ==, 65);
closeFont();
cleanupFreeType();
}
static void
test_get_glyph_italics_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_math_get_glyph_italics_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_math_get_glyph_italics_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_math_get_glyph_italics_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_math_get_glyph_italics_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_math_get_glyph_italics_correction (hb_font, glyph), ==, 394);
g_assert(hb_font_get_glyph_from_name (hb_font, "B", -1, &glyph));
g_assert_cmpint(hb_ot_math_get_glyph_italics_correction (hb_font, glyph), ==, 300);
g_assert(hb_font_get_glyph_from_name (hb_font, "C", -1, &glyph));
g_assert_cmpint(hb_ot_math_get_glyph_italics_correction (hb_font, glyph), ==, 904);
closeFont();
cleanupFreeType();
}
static void
test_get_glyph_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_math_get_glyph_top_accent_attachment (hb_font, glyph), ==, 1000); // 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_math_get_glyph_top_accent_attachment (hb_font, glyph), ==, 1000); // 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_math_get_glyph_top_accent_attachment (hb_font, glyph), ==, 1000); // 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_math_get_glyph_top_accent_attachment (hb_font, glyph), ==, 1000); // Glyph without top accent attachment.
g_assert(hb_font_get_glyph_from_name (hb_font, "D", -1, &glyph));
g_assert_cmpint(hb_ot_math_get_glyph_top_accent_attachment (hb_font, glyph), ==, 748);
g_assert(hb_font_get_glyph_from_name (hb_font, "E", -1, &glyph));
g_assert_cmpint(hb_ot_math_get_glyph_top_accent_attachment (hb_font, glyph), ==, 692);
g_assert(hb_font_get_glyph_from_name (hb_font, "F", -1, &glyph));
g_assert_cmpint(hb_ot_math_get_glyph_top_accent_attachment (hb_font, glyph), ==, 636);
closeFont();
cleanupFreeType();
}
static void
test_is_glyph_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_math_is_glyph_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_math_is_glyph_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_math_is_glyph_extended_shape (hb_face, glyph));
g_assert(hb_font_get_glyph_from_name (hb_font, "H", -1, &glyph));
g_assert(hb_ot_math_is_glyph_extended_shape (hb_face, glyph));
closeFont();
cleanupFreeType();
}
static void
test_get_glyph_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_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 0), ==, 0); // MathGlyphInfo not available
g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_LEFT, 0), ==, 0); // MathGlyphInfo not available
g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_BOTTOM_RIGHT, 0), ==, 0); // MathGlyphInfo not available
g_assert_cmpint(hb_ot_math_get_glyph_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_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 0), ==, 0); // MathKernInfo empty
g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_LEFT, 0), ==, 0); // MathKernInfo empty
g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_BOTTOM_RIGHT, 0), ==, 0); // MathKernInfo empty
g_assert_cmpint(hb_ot_math_get_glyph_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_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 0), ==, 0); // MathKernInfoRecords empty
g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_LEFT, 0), ==, 0); // MathKernInfoRecords empty
g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_BOTTOM_RIGHT, 0), ==, 0); // MathKernInfoRecords empty
g_assert_cmpint(hb_ot_math_get_glyph_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_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 7), ==, 62); // lower than min heigth
g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 14), ==, 62); // equal to min height
g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 20), ==, 104);
g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 23), ==, 104);
g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 31), ==, 146);
g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 32), ==, 146);
g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 86), ==, 398); // equal to max height
g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 91), ==, 440); // larger than max height
g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 96), ==, 440); // larger than max height
g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 39), ==, 188); // top right
g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_LEFT, 39), ==, 110); // top left
g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_BOTTOM_RIGHT, 39), ==, 44); // bottom right
g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_BOTTOM_LEFT, 39), ==, 100); // bottom left
closeFont();
cleanupFreeType();
}
static hb_position_t
get_glyph_assembly_italics_correction (hb_font_t *font,
hb_codepoint_t glyph,
hb_bool_t horizontal)
{
hb_position_t corr;
hb_ot_math_get_glyph_assembly (font, glyph,
horizontal ? HB_DIRECTION_LTR : HB_DIRECTION_TTB,
0, NULL, NULL,
&corr);
return corr;
}
static void
test_get_glyph_assembly_italics_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(get_glyph_assembly_italics_correction (hb_font, glyph, TRUE), ==, 0); // MathVariants not available
g_assert_cmpint(get_glyph_assembly_italics_correction (hb_font, glyph, FALSE), ==, 0); // MathVariants not available
closeFont();
openFont("fonts/MathTestFontPartial1.otf");
g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
g_assert_cmpint(get_glyph_assembly_italics_correction (hb_font, glyph, TRUE), ==, 0); // VertGlyphCoverage and HorizGlyphCoverage absent
g_assert_cmpint(get_glyph_assembly_italics_correction (hb_font, glyph, FALSE), ==, 0); // VertGlyphCoverage and HorizGlyphCoverage absent
closeFont();
openFont("fonts/MathTestFontPartial2.otf");
g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
g_assert_cmpint(get_glyph_assembly_italics_correction (hb_font, glyph, TRUE), ==, 0); // VertGlyphCoverage and HorizGlyphCoverage empty
g_assert_cmpint(get_glyph_assembly_italics_correction (hb_font, glyph, FALSE), ==, 0); // VertGlyphCoverage and HorizGlyphCoverage empty
closeFont();
openFont("fonts/MathTestFontPartial3.otf");
g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
g_assert_cmpint(get_glyph_assembly_italics_correction (hb_font, glyph, TRUE), ==, 0); // HorizGlyphConstruction and VertGlyphConstruction empty
g_assert_cmpint(get_glyph_assembly_italics_correction (hb_font, glyph, FALSE), ==, 0); // HorizGlyphConstruction and VertGlyphConstruction empty
closeFont();
openFont("fonts/MathTestFontPartial4.otf");
g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
g_assert_cmpint(get_glyph_assembly_italics_correction (hb_font, glyph, TRUE), ==, 0);
g_assert_cmpint(get_glyph_assembly_italics_correction (hb_font, glyph, FALSE), ==, 0);
closeFont();
openFont("fonts/MathTestFontFull.otf");
g_assert(hb_font_get_glyph_from_name (hb_font, "arrowleft", -1, &glyph));
g_assert_cmpint(get_glyph_assembly_italics_correction (hb_font, glyph, TRUE), ==, 248);
g_assert_cmpint(get_glyph_assembly_italics_correction (hb_font, glyph, FALSE), ==, 0);
g_assert(hb_font_get_glyph_from_name (hb_font, "arrowup", -1, &glyph));
g_assert_cmpint(get_glyph_assembly_italics_correction (hb_font, glyph, TRUE), ==, 0);
g_assert_cmpint(get_glyph_assembly_italics_correction (hb_font, glyph, FALSE), ==, 662);
closeFont();
cleanupFreeType();
}
static void
test_get_min_connector_overlap (void)
{
initFreeType();
openFont("fonts/MathTestFontEmpty.otf");
g_assert_cmpint(hb_ot_math_get_min_connector_overlap(hb_font, FALSE), ==, 0); // MathVariants not available
g_assert_cmpint(hb_ot_math_get_min_connector_overlap(hb_font, TRUE), ==, 0); // MathVariants not available
closeFont();
openFont("fonts/MathTestFontPartial1.otf");
g_assert_cmpint(hb_ot_math_get_min_connector_overlap(hb_font, HB_DIRECTION_LTR), ==, 108);
g_assert_cmpint(hb_ot_math_get_min_connector_overlap(hb_font, HB_DIRECTION_TTB), ==, 54);
closeFont();
cleanupFreeType();
}
static void
test_get_glyph_variants (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_math_get_glyph_variants (hb_font, glyph, HB_DIRECTION_RTL, 0, NULL, NULL), ==, 0);
g_assert_cmpint(hb_ot_math_get_glyph_variants (hb_font, glyph, HB_DIRECTION_BTT, 0, NULL, NULL), ==, 0);
closeFont();
openFont("fonts/MathTestFontPartial1.otf");
g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
g_assert_cmpint(hb_ot_math_get_glyph_variants (hb_font, glyph, HB_DIRECTION_RTL, 0, NULL, NULL), ==, 0);
g_assert_cmpint(hb_ot_math_get_glyph_variants (hb_font, glyph, HB_DIRECTION_BTT, 0, NULL, NULL), ==, 0);
closeFont();
openFont("fonts/MathTestFontPartial2.otf");
g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
g_assert_cmpint(hb_ot_math_get_glyph_variants (hb_font, glyph, HB_DIRECTION_RTL, 0, NULL, NULL), ==, 0);
g_assert_cmpint(hb_ot_math_get_glyph_variants (hb_font, glyph, HB_DIRECTION_BTT, 0, NULL, NULL), ==, 0);
closeFont();
openFont("fonts/MathTestFontPartial3.otf");
g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
g_assert_cmpint(hb_ot_math_get_glyph_variants (hb_font, glyph, HB_DIRECTION_RTL, 0, NULL, NULL), ==, 0);
g_assert_cmpint(hb_ot_math_get_glyph_variants (hb_font, glyph, HB_DIRECTION_BTT, 0, NULL, NULL), ==, 0);
closeFont();
openFont("fonts/MathTestFontPartial4.otf");
g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
g_assert_cmpint(hb_ot_math_get_glyph_variants (hb_font, glyph, HB_DIRECTION_RTL, 0, NULL, NULL), ==, 0);
g_assert_cmpint(hb_ot_math_get_glyph_variants (hb_font, glyph, HB_DIRECTION_BTT, 0, NULL, NULL), ==, 0);
closeFont();
openFont("fonts/MathTestFontFull.otf");
g_assert(hb_font_get_glyph_from_name (hb_font, "arrowleft", -1, &glyph));
g_assert_cmpint(hb_ot_math_get_glyph_variants (hb_font,
glyph,
HB_DIRECTION_BTT,
0,
NULL,
NULL), ==, 0);
g_assert_cmpint(hb_ot_math_get_glyph_variants (hb_font,
glyph,
HB_DIRECTION_RTL,
0,
NULL,
NULL), ==, 3);
g_assert(hb_font_get_glyph_from_name (hb_font, "arrowup", -1, &glyph));
g_assert_cmpint(hb_ot_math_get_glyph_variants (hb_font,
glyph,
HB_DIRECTION_BTT,
0,
NULL,
NULL), ==, 4);
g_assert_cmpint(hb_ot_math_get_glyph_variants (hb_font,
glyph,
HB_DIRECTION_RTL,
0,
NULL,
NULL), ==, 0);
g_assert(hb_font_get_glyph_from_name (hb_font, "arrowleft", -1, &glyph));
hb_ot_math_glyph_variant_t variants[20];
unsigned variantsSize = sizeof (variants) / sizeof (variants[0]);
unsigned int count;
unsigned int offset = 0;
do {
count = variantsSize;
hb_ot_math_get_glyph_variants (hb_font,
glyph,
HB_DIRECTION_RTL,
offset,
&count,
variants);
offset += count;
} while (count == variantsSize);
g_assert_cmpint(offset, ==, 3);
g_assert(hb_font_get_glyph_from_name (hb_font, "uni2190_size2", -1, &glyph));
g_assert_cmpint(variants[0].glyph, ==, glyph);
g_assert_cmpint(variants[0].advance, ==, 4302);
g_assert(hb_font_get_glyph_from_name (hb_font, "uni2190_size3", -1, &glyph));
g_assert_cmpint(variants[1].glyph, ==, glyph);
g_assert_cmpint(variants[1].advance, ==, 4802);
g_assert(hb_font_get_glyph_from_name (hb_font, "uni2190_size4", -1, &glyph));
g_assert_cmpint(variants[2].glyph, ==, glyph);
g_assert_cmpint(variants[2].advance, ==, 5802);
g_assert(hb_font_get_glyph_from_name (hb_font, "arrowup", -1, &glyph));
offset = 0;
do {
count = variantsSize;
hb_ot_math_get_glyph_variants (hb_font,
glyph,
HB_DIRECTION_BTT,
offset,
&count,
variants);
offset += count;
} while (count == variantsSize);
g_assert_cmpint(offset, ==, 4);
g_assert(hb_font_get_glyph_from_name (hb_font, "uni2191_size2", -1, &glyph));
g_assert_cmpint(variants[0].glyph, ==, glyph);
g_assert_cmpint(variants[0].advance, ==, 2251);
g_assert(hb_font_get_glyph_from_name (hb_font, "uni2191_size3", -1, &glyph));
g_assert_cmpint(variants[1].glyph, ==, glyph);
g_assert_cmpint(variants[1].advance, ==, 2501);
g_assert(hb_font_get_glyph_from_name (hb_font, "uni2191_size4", -1, &glyph));
g_assert_cmpint(variants[2].glyph, ==, glyph);
g_assert_cmpint(variants[2].advance, ==, 3001);
g_assert(hb_font_get_glyph_from_name (hb_font, "uni2191_size5", -1, &glyph));
g_assert_cmpint(variants[3].glyph, ==, glyph);
g_assert_cmpint(variants[3].advance, ==, 3751);
closeFont();
cleanupFreeType();
}
static void
test_get_glyph_assembly (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_math_get_glyph_assembly (hb_font, glyph, HB_DIRECTION_RTL, 0, NULL, NULL, NULL), ==, 0);
g_assert_cmpint(hb_ot_math_get_glyph_assembly (hb_font, glyph, HB_DIRECTION_BTT, 0, NULL, NULL, NULL), ==, 0);
closeFont();
openFont("fonts/MathTestFontPartial1.otf");
g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
g_assert_cmpint(hb_ot_math_get_glyph_assembly (hb_font, glyph, HB_DIRECTION_RTL, 0, NULL, NULL, NULL), ==, 0);
g_assert_cmpint(hb_ot_math_get_glyph_assembly (hb_font, glyph, HB_DIRECTION_BTT, 0, NULL, NULL, NULL), ==, 0);
closeFont();
openFont("fonts/MathTestFontPartial2.otf");
g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
g_assert_cmpint(hb_ot_math_get_glyph_assembly (hb_font, glyph, HB_DIRECTION_RTL, 0, NULL, NULL, NULL), ==, 0);
g_assert_cmpint(hb_ot_math_get_glyph_assembly (hb_font, glyph, HB_DIRECTION_BTT, 0, NULL, NULL, NULL), ==, 0);
closeFont();
openFont("fonts/MathTestFontPartial3.otf");
g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
g_assert_cmpint(hb_ot_math_get_glyph_assembly (hb_font, glyph, HB_DIRECTION_RTL, 0, NULL, NULL, NULL), ==, 0);
g_assert_cmpint(hb_ot_math_get_glyph_assembly (hb_font, glyph, HB_DIRECTION_BTT, 0, NULL, NULL, NULL), ==, 0);
closeFont();
openFont("fonts/MathTestFontPartial4.otf");
g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
g_assert_cmpint(hb_ot_math_get_glyph_assembly (hb_font, glyph, HB_DIRECTION_RTL, 0, NULL, NULL, NULL), ==, 0);
g_assert_cmpint(hb_ot_math_get_glyph_assembly (hb_font, glyph, HB_DIRECTION_BTT, 0, NULL, NULL, NULL), ==, 0);
closeFont();
openFont("fonts/MathTestFontFull.otf");
g_assert(hb_font_get_glyph_from_name (hb_font, "arrowright", -1, &glyph));
g_assert_cmpint(hb_ot_math_get_glyph_assembly (hb_font,
glyph,
HB_DIRECTION_BTT,
0,
NULL,
NULL,
NULL), ==, 0);
g_assert_cmpint(hb_ot_math_get_glyph_assembly (hb_font,
glyph,
HB_DIRECTION_RTL,
0,
NULL,
NULL,
NULL), ==, 3);
g_assert(hb_font_get_glyph_from_name (hb_font, "arrowdown", -1, &glyph));
g_assert_cmpint(hb_ot_math_get_glyph_assembly (hb_font,
glyph,
HB_DIRECTION_BTT,
0,
NULL,
NULL,
NULL), ==, 5);
g_assert_cmpint(hb_ot_math_get_glyph_assembly (hb_font,
glyph,
HB_DIRECTION_RTL,
0,
NULL,
NULL,
NULL), ==, 0);
g_assert(hb_font_get_glyph_from_name (hb_font, "arrowright", -1, &glyph));
hb_ot_math_glyph_part_t parts[20];
unsigned partsSize = sizeof (parts) / sizeof (parts[0]);
unsigned int count;
unsigned int offset = 0;
do {
count = partsSize;
hb_ot_math_get_glyph_assembly (hb_font,
glyph,
HB_DIRECTION_RTL,
offset,
&count,
parts,
NULL);
offset += count;
} while (count == partsSize);
g_assert_cmpint(offset, ==, 3);
g_assert(hb_font_get_glyph_from_name (hb_font, "left", -1, &glyph));
g_assert_cmpint(parts[0].glyph, ==, glyph);
g_assert_cmpint(parts[0].start_connector_length, ==, 800);
g_assert_cmpint(parts[0].end_connector_length, ==, 384);
g_assert_cmpint(parts[0].full_advance, ==, 2000);
g_assert(!(parts[0].flags & HB_MATH_GLYPH_PART_FLAG_EXTENDER));
g_assert(hb_font_get_glyph_from_name (hb_font, "horizontal", -1, &glyph));
g_assert_cmpint(parts[1].glyph, ==, glyph);
g_assert_cmpint(parts[1].start_connector_length, ==, 524);
g_assert_cmpint(parts[1].end_connector_length, ==, 800);
g_assert_cmpint(parts[1].full_advance, ==, 2000);
g_assert(parts[1].flags & HB_MATH_GLYPH_PART_FLAG_EXTENDER);
g_assert(hb_font_get_glyph_from_name (hb_font, "right", -1, &glyph));
g_assert_cmpint(parts[2].glyph, ==, glyph);
g_assert_cmpint(parts[2].start_connector_length, ==, 316);
g_assert_cmpint(parts[2].end_connector_length, ==, 454);
g_assert_cmpint(parts[2].full_advance, ==, 2000);
g_assert(!(parts[2].flags & HB_MATH_GLYPH_PART_FLAG_EXTENDER));
g_assert(hb_font_get_glyph_from_name (hb_font, "arrowdown", -1, &glyph));
offset = 0;
do {
count = partsSize;
hb_ot_math_get_glyph_assembly (hb_font,
glyph,
HB_DIRECTION_BTT,
offset,
&count,
parts,
NULL);
offset += count;
} while (count == partsSize);
g_assert_cmpint(offset, ==, 5);
g_assert(hb_font_get_glyph_from_name (hb_font, "bottom", -1, &glyph));
g_assert_cmpint(parts[0].glyph, ==, glyph);
g_assert_cmpint(parts[0].start_connector_length, ==, 365);
g_assert_cmpint(parts[0].end_connector_length, ==, 158);
g_assert_cmpint(parts[0].full_advance, ==, 1000);
g_assert(!(parts[0].flags & HB_MATH_GLYPH_PART_FLAG_EXTENDER));
g_assert(hb_font_get_glyph_from_name (hb_font, "vertical", -1, &glyph));
g_assert_cmpint(parts[1].glyph, ==, glyph);
g_assert_cmpint(parts[1].glyph, ==, glyph);
g_assert_cmpint(parts[1].start_connector_length, ==, 227);
g_assert_cmpint(parts[1].end_connector_length, ==, 365);
g_assert_cmpint(parts[1].full_advance, ==, 1000);
g_assert(parts[1].flags & HB_MATH_GLYPH_PART_FLAG_EXTENDER);
g_assert(hb_font_get_glyph_from_name (hb_font, "center", -1, &glyph));
g_assert_cmpint(parts[2].glyph, ==, glyph);
g_assert_cmpint(parts[2].start_connector_length, ==, 54);
g_assert_cmpint(parts[2].end_connector_length, ==, 158);
g_assert_cmpint(parts[2].full_advance, ==, 1000);
g_assert(!(parts[2].flags & HB_MATH_GLYPH_PART_FLAG_EXTENDER));
g_assert(hb_font_get_glyph_from_name (hb_font, "vertical", -1, &glyph));
g_assert_cmpint(parts[3].glyph, ==, glyph);
g_assert_cmpint(parts[3].glyph, ==, glyph);
g_assert_cmpint(parts[3].glyph, ==, glyph);
g_assert_cmpint(parts[3].start_connector_length, ==, 400);
g_assert_cmpint(parts[3].end_connector_length, ==, 296);
g_assert_cmpint(parts[3].full_advance, ==, 1000);
g_assert(parts[1].flags & HB_MATH_GLYPH_PART_FLAG_EXTENDER);
g_assert(hb_font_get_glyph_from_name (hb_font, "top", -1, &glyph));
g_assert_cmpint(parts[4].glyph, ==, glyph);
g_assert_cmpint(parts[4].start_connector_length, ==, 123);
g_assert_cmpint(parts[4].end_connector_length, ==, 192);
g_assert_cmpint(parts[4].full_advance, ==, 1000);
g_assert(!(parts[4].flags & HB_MATH_GLYPH_PART_FLAG_EXTENDER));
closeFont();
cleanupFreeType();
}
int
main (int argc, char **argv)
{
hb_test_init (&argc, &argv);
hb_test_add (test_has_data);
hb_test_add (test_get_constant);
hb_test_add (test_get_glyph_italics_correction);
hb_test_add (test_get_glyph_top_accent_attachment);
hb_test_add (test_is_glyph_extended_shape);
hb_test_add (test_get_glyph_kerning);
hb_test_add (test_get_glyph_assembly_italics_correction);
hb_test_add (test_get_min_connector_overlap);
hb_test_add (test_get_glyph_variants);
hb_test_add (test_get_glyph_assembly);
return hb_test_run();
}