From da1c91202591a4191b78a937b76f56a443dde230 Mon Sep 17 00:00:00 2001 From: Khaled Hosny Date: Mon, 25 Dec 2023 16:51:17 +0200 Subject: [PATCH 1/6] Minor formatting consistency --- src/uharfbuzz/charfbuzz.pxd | 166 ++++++++++++++++++------------------ 1 file changed, 83 insertions(+), 83 deletions(-) diff --git a/src/uharfbuzz/charfbuzz.pxd b/src/uharfbuzz/charfbuzz.pxd index 6af4627..b7c6680 100644 --- a/src/uharfbuzz/charfbuzz.pxd +++ b/src/uharfbuzz/charfbuzz.pxd @@ -46,7 +46,7 @@ cdef extern from "hb.h": void hb_tag_to_string(hb_tag_t tag, char* buf) hb_language_t hb_ot_tag_to_language(hb_tag_t tag) hb_script_t hb_ot_tag_to_script(hb_tag_t tag) - const char* hb_version_string(); + const char* hb_version_string() ctypedef struct hb_user_data_key_t: pass @@ -147,8 +147,8 @@ cdef extern from "hb.h": hb_buffer_t* hb_buffer_create() hb_bool_t hb_buffer_allocation_successful(hb_buffer_t* buffer) - void hb_buffer_reset (hb_buffer_t *buffer) - void hb_buffer_clear_contents (hb_buffer_t *buffer) + void hb_buffer_reset(hb_buffer_t *buffer) + void hb_buffer_clear_contents(hb_buffer_t *buffer) void hb_buffer_add_codepoints( hb_buffer_t* buffer, const hb_codepoint_t* text, int text_length, @@ -172,7 +172,7 @@ cdef extern from "hb.h": void hb_buffer_guess_segment_properties(hb_buffer_t* buffer) hb_direction_t hb_buffer_get_direction(hb_buffer_t* buffer) void hb_buffer_set_direction(hb_buffer_t* buffer, hb_direction_t direction) - unsigned int hb_buffer_get_length (const hb_buffer_t *buffer) + unsigned int hb_buffer_get_length(const hb_buffer_t *buffer) hb_glyph_info_t* hb_buffer_get_glyph_infos( hb_buffer_t* buffer, unsigned int* length) hb_glyph_position_t* hb_buffer_get_glyph_positions( @@ -190,21 +190,21 @@ cdef extern from "hb.h": hb_font_t *font, const char *message, void *user_data) - void hb_buffer_set_message_func ( + void hb_buffer_set_message_func( hb_buffer_t *buffer, hb_buffer_message_func_t func, void *user_data, void* destroy) - void hb_buffer_set_flags (hb_buffer_t *buffer, hb_buffer_flags_t flags) - hb_buffer_flags_t hb_buffer_get_flags (const hb_buffer_t *buffer) - void hb_buffer_set_content_type (hb_buffer_t *buffer, hb_buffer_content_type_t content_type) - hb_buffer_content_type_t hb_buffer_get_content_type (const hb_buffer_t *buffer) - void hb_buffer_set_replacement_codepoint (hb_buffer_t *buffer, hb_codepoint_t replacement) - hb_codepoint_t hb_buffer_get_replacement_codepoint (const hb_buffer_t *buffer) - void hb_buffer_set_invisible_glyph (hb_buffer_t *buffer, hb_codepoint_t invisible) - hb_codepoint_t hb_buffer_get_invisible_glyph (const hb_buffer_t *buffer) - void hb_buffer_set_not_found_glyph (hb_buffer_t *buffer, hb_codepoint_t not_found) - hb_codepoint_t hb_buffer_get_not_found_glyph (const hb_buffer_t *buffer) + void hb_buffer_set_flags(hb_buffer_t *buffer, hb_buffer_flags_t flags) + hb_buffer_flags_t hb_buffer_get_flags(const hb_buffer_t *buffer) + void hb_buffer_set_content_type(hb_buffer_t *buffer, hb_buffer_content_type_t content_type) + hb_buffer_content_type_t hb_buffer_get_content_type(const hb_buffer_t *buffer) + void hb_buffer_set_replacement_codepoint(hb_buffer_t *buffer, hb_codepoint_t replacement) + hb_codepoint_t hb_buffer_get_replacement_codepoint(const hb_buffer_t *buffer) + void hb_buffer_set_invisible_glyph(hb_buffer_t *buffer, hb_codepoint_t invisible) + hb_codepoint_t hb_buffer_get_invisible_glyph(const hb_buffer_t *buffer) + void hb_buffer_set_not_found_glyph(hb_buffer_t *buffer, hb_codepoint_t not_found) + hb_codepoint_t hb_buffer_get_not_found_glyph(const hb_buffer_t *buffer) # hb-face.h @@ -217,12 +217,12 @@ cdef extern from "hb.h": hb_face_t* hb_face_create_for_tables( hb_reference_table_func_t reference_table_func, void* user_data, hb_destroy_func_t destroy) - unsigned int hb_face_get_index (const hb_face_t* face) - void hb_face_set_index (hb_face_t* face, unsigned int index) + unsigned int hb_face_get_index(const hb_face_t* face) + void hb_face_set_index(hb_face_t* face, unsigned int index) unsigned int hb_face_get_upem(hb_face_t* face) void hb_face_set_upem(hb_face_t* face, unsigned int upem) - unsigned int hb_face_get_glyph_count (hb_face_t* face); - void hb_face_set_glyph_count (hb_face_t* face, unsigned int glyph_count) + unsigned int hb_face_get_glyph_count(hb_face_t* face) + void hb_face_set_glyph_count(hb_face_t* face, unsigned int glyph_count) void* hb_face_get_user_data(hb_face_t* face, hb_user_data_key_t* key) hb_bool_t hb_face_set_user_data( hb_face_t* face, @@ -230,16 +230,16 @@ cdef extern from "hb.h": void* data, hb_destroy_func_t destroy, hb_bool_t replace) void hb_face_destroy(hb_face_t* face) - hb_blob_t* hb_face_reference_blob (hb_face_t *face) + hb_blob_t* hb_face_reference_blob(hb_face_t *face) hb_face_t* hb_face_get_empty() - unsigned int hb_face_get_table_tags ( + unsigned int hb_face_get_table_tags( const hb_face_t *face, unsigned int start_offset, unsigned int *table_count, hb_tag_t *table_tags) - void hb_face_collect_unicodes (hb_face_t *face, hb_set_t *out) - void hb_face_collect_variation_selectors (hb_face_t *face, hb_set_t *out) - void hb_face_collect_variation_unicodes (hb_face_t *face, hb_codepoint_t variation_selector, hb_set_t *out) + void hb_face_collect_unicodes(hb_face_t *face, hb_set_t *out) + void hb_face_collect_variation_selectors(hb_face_t *face, hb_set_t *out) + void hb_face_collect_variation_unicodes(hb_face_t *face, hb_codepoint_t variation_selector, hb_set_t *out) # hb-font.h @@ -251,19 +251,19 @@ cdef extern from "hb.h": hb_font_t* font, void* font_data, hb_codepoint_t unicode, hb_codepoint_t* glyph, - void* user_data); + void* user_data) ctypedef hb_position_t (*hb_font_get_glyph_advance_func_t) ( hb_font_t* font, void* font_data, hb_codepoint_t glyph, void* user_data) - ctypedef hb_font_get_glyph_advance_func_t hb_font_get_glyph_h_advance_func_t; - ctypedef hb_font_get_glyph_advance_func_t hb_font_get_glyph_v_advance_func_t; + ctypedef hb_font_get_glyph_advance_func_t hb_font_get_glyph_h_advance_func_t + ctypedef hb_font_get_glyph_advance_func_t hb_font_get_glyph_v_advance_func_t ctypedef hb_bool_t (*hb_font_get_glyph_origin_func_t) ( hb_font_t* font, void* font_data, hb_codepoint_t glyph, hb_position_t* x, hb_position_t* y, void* user_data) - ctypedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_v_origin_func_t; + ctypedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_v_origin_func_t ctypedef hb_bool_t (*hb_font_get_glyph_name_func_t) ( hb_font_t *font, void *font_data, hb_codepoint_t glyph, @@ -273,8 +273,8 @@ cdef extern from "hb.h": hb_font_t *font, void *font_data, hb_font_extents_t *extents, void *user_data) - ctypedef hb_font_get_font_extents_func_t hb_font_get_font_h_extents_func_t; - ctypedef hb_font_get_font_extents_func_t hb_font_get_font_v_extents_func_t; + ctypedef hb_font_get_font_extents_func_t hb_font_get_font_h_extents_func_t + ctypedef hb_font_get_font_extents_func_t hb_font_get_font_v_extents_func_t ctypedef struct hb_variation_t: hb_tag_t tag float value @@ -305,7 +305,7 @@ cdef extern from "hb.h": hb_font_funcs_t* ffuncs, hb_font_get_glyph_v_origin_func_t func, void* user_data, hb_destroy_func_t destroy) - void hb_font_funcs_set_glyph_name_func ( + void hb_font_funcs_set_glyph_name_func( hb_font_funcs_t* ffuncs, hb_font_get_glyph_name_func_t func, void* user_data, hb_destroy_func_t destroy) @@ -335,16 +335,16 @@ cdef extern from "hb.h": void hb_font_set_ppem(hb_font_t* font, unsigned int x_ppem, unsigned int y_ppem) float hb_font_get_ptem(hb_font_t* font) void hb_font_set_ptem(hb_font_t* font, float ptem) - void hb_font_get_synthetic_bold (hb_font_t *font, + void hb_font_get_synthetic_bold(hb_font_t *font, float *x_embolden, float *y_embolden, hb_bool_t *in_place) - void hb_font_set_synthetic_bold (hb_font_t *font, + void hb_font_set_synthetic_bold(hb_font_t *font, float x_embolden, float y_embolden, hb_bool_t in_place) - float hb_font_get_synthetic_slant (hb_font_t *font) - void hb_font_set_synthetic_slant (hb_font_t *font, float slant) + float hb_font_get_synthetic_slant(hb_font_t *font) + void hb_font_set_synthetic_slant(hb_font_t *font, float slant) void hb_font_set_variations( hb_font_t* font, const hb_variation_t* variations, @@ -352,9 +352,9 @@ cdef extern from "hb.h": void hb_font_set_variation( hb_font_t *font, hb_tag_t tag, - float value); - void hb_font_set_var_named_instance (hb_font_t *font, unsigned int instance_index) - unsigned int hb_font_get_var_named_instance (hb_font_t *font) + float value) + void hb_font_set_var_named_instance(hb_font_t *font, unsigned int instance_index) + unsigned int hb_font_get_var_named_instance(hb_font_t *font) void hb_font_set_var_coords_design( hb_font_t *font, const float *coords, @@ -446,7 +446,7 @@ cdef extern from "hb.h": hb_draw_state_t *st, float to_x, float to_y, - void *user_data); + void *user_data) ctypedef void (*hb_draw_line_to_func_t) ( hb_draw_funcs_t *dfuncs, @@ -454,7 +454,7 @@ cdef extern from "hb.h": hb_draw_state_t *st, float to_x, float to_y, - void *user_data); + void *user_data) ctypedef void (*hb_draw_quadratic_to_func_t) ( hb_draw_funcs_t *dfuncs, @@ -464,7 +464,7 @@ cdef extern from "hb.h": float control_y, float to_x, float to_y, - void *user_data); + void *user_data) ctypedef void (*hb_draw_cubic_to_func_t) ( hb_draw_funcs_t *dfuncs, @@ -476,33 +476,33 @@ cdef extern from "hb.h": float control2_y, float to_x, float to_y, - void *user_data); + void *user_data) ctypedef void (*hb_draw_close_path_func_t) ( hb_draw_funcs_t *dfuncs, void *draw_data, hb_draw_state_t *st, - void *user_data); + void *user_data) - void hb_draw_funcs_set_move_to_func ( + void hb_draw_funcs_set_move_to_func( hb_draw_funcs_t* dfuncs, hb_draw_move_to_func_t func, void *user_data, hb_destroy_func_t destroy) - void hb_draw_funcs_set_line_to_func ( + void hb_draw_funcs_set_line_to_func( hb_draw_funcs_t* dfuncs, hb_draw_line_to_func_t func, void *user_data, hb_destroy_func_t destroy) - void hb_draw_funcs_set_quadratic_to_func ( + void hb_draw_funcs_set_quadratic_to_func( hb_draw_funcs_t* dfuncs, hb_draw_quadratic_to_func_t func, void *user_data, hb_destroy_func_t destroy) - void hb_draw_funcs_set_cubic_to_func ( + void hb_draw_funcs_set_cubic_to_func( hb_draw_funcs_t* dfuncs, hb_draw_cubic_to_func_t func, void *user_data, @@ -530,7 +530,7 @@ cdef extern from "hb.h": hb_buffer_t* buffer, const hb_feature_t* features, unsigned int num_features) - hb_bool_t hb_shape_full ( + hb_bool_t hb_shape_full( hb_font_t *font, hb_buffer_t *buffer, const hb_feature_t *features, @@ -566,40 +566,40 @@ cdef extern from "hb.h": ctypedef struct hb_set_t: pass cdef hb_codepoint_t HB_SET_VALUE_INVALID - hb_set_t* hb_set_create(); - hb_set_t* hb_set_get_empty(); - hb_set_t* hb_set_reference(hb_set_t* set); - void hb_set_destroy(hb_set_t* set); - hb_bool_t hb_set_set_user_data(hb_set_t* set, hb_user_data_key_t* key, void* data, hb_destroy_func_t destroy, hb_bool_t replace); - void* hb_set_get_user_data(const hb_set_t* set, hb_user_data_key_t* key); - hb_bool_t hb_set_allocation_successful(const hb_set_t* set); - hb_set_t* hb_set_copy(const hb_set_t* set); - void hb_set_clear(hb_set_t* set); - hb_bool_t hb_set_is_empty(const hb_set_t* set); - void hb_set_invert(hb_set_t* set); - hb_bool_t hb_set_is_inverted(const hb_set_t* set); - hb_bool_t hb_set_has(const hb_set_t* set, hb_codepoint_t codepoint); - void hb_set_add(hb_set_t* set, hb_codepoint_t codepoint); - void hb_set_add_range(hb_set_t* set, hb_codepoint_t first, hb_codepoint_t last); - void hb_set_add_sorted_array(hb_set_t* set, const hb_codepoint_t* sorted_codepoints, unsigned int num_codepoints); - void hb_set_del(hb_set_t* set, hb_codepoint_t codepoint); - void hb_set_del_range(hb_set_t* set, hb_codepoint_t first, hb_codepoint_t last); - hb_bool_t hb_set_is_equal(const hb_set_t* set, const hb_set_t* other); - unsigned int hb_set_hash(const hb_set_t* set); - hb_bool_t hb_set_is_subset(const hb_set_t* set, const hb_set_t* larger_set); - void hb_set_set(hb_set_t* set, const hb_set_t* other); - void hb_set_union(hb_set_t* set, const hb_set_t* other); - void hb_set_intersect(hb_set_t* set, const hb_set_t* other); - void hb_set_subtract(hb_set_t* set, const hb_set_t* other); - void hb_set_symmetric_difference(hb_set_t* set, const hb_set_t* other); - unsigned int hb_set_get_population(const hb_set_t* set); - hb_codepoint_t hb_set_get_min(const hb_set_t* set); - hb_codepoint_t hb_set_get_max(const hb_set_t* set); - hb_bool_t hb_set_next(const hb_set_t* set, hb_codepoint_t* codepoint); - hb_bool_t hb_set_previous(const hb_set_t* set, hb_codepoint_t* codepoint); - hb_bool_t hb_set_next_range(const hb_set_t* set, hb_codepoint_t* first, hb_codepoint_t* last); - hb_bool_t hb_set_previous_range(const hb_set_t* set, hb_codepoint_t* first, hb_codepoint_t* last); - unsigned int hb_set_next_many(const hb_set_t* set, hb_codepoint_t codepoint, hb_codepoint_t* out, unsigned int size); + hb_set_t* hb_set_create() + hb_set_t* hb_set_get_empty() + hb_set_t* hb_set_reference(hb_set_t* set) + void hb_set_destroy(hb_set_t* set) + hb_bool_t hb_set_set_user_data(hb_set_t* set, hb_user_data_key_t* key, void* data, hb_destroy_func_t destroy, hb_bool_t replace) + void* hb_set_get_user_data(const hb_set_t* set, hb_user_data_key_t* key) + hb_bool_t hb_set_allocation_successful(const hb_set_t* set) + hb_set_t* hb_set_copy(const hb_set_t* set) + void hb_set_clear(hb_set_t* set) + hb_bool_t hb_set_is_empty(const hb_set_t* set) + void hb_set_invert(hb_set_t* set) + hb_bool_t hb_set_is_inverted(const hb_set_t* set) + hb_bool_t hb_set_has(const hb_set_t* set, hb_codepoint_t codepoint) + void hb_set_add(hb_set_t* set, hb_codepoint_t codepoint) + void hb_set_add_range(hb_set_t* set, hb_codepoint_t first, hb_codepoint_t last) + void hb_set_add_sorted_array(hb_set_t* set, const hb_codepoint_t* sorted_codepoints, unsigned int num_codepoints) + void hb_set_del(hb_set_t* set, hb_codepoint_t codepoint) + void hb_set_del_range(hb_set_t* set, hb_codepoint_t first, hb_codepoint_t last) + hb_bool_t hb_set_is_equal(const hb_set_t* set, const hb_set_t* other) + unsigned int hb_set_hash(const hb_set_t* set) + hb_bool_t hb_set_is_subset(const hb_set_t* set, const hb_set_t* larger_set) + void hb_set_set(hb_set_t* set, const hb_set_t* other) + void hb_set_union(hb_set_t* set, const hb_set_t* other) + void hb_set_intersect(hb_set_t* set, const hb_set_t* other) + void hb_set_subtract(hb_set_t* set, const hb_set_t* other) + void hb_set_symmetric_difference(hb_set_t* set, const hb_set_t* other) + unsigned int hb_set_get_population(const hb_set_t* set) + hb_codepoint_t hb_set_get_min(const hb_set_t* set) + hb_codepoint_t hb_set_get_max(const hb_set_t* set) + hb_bool_t hb_set_next(const hb_set_t* set, hb_codepoint_t* codepoint) + hb_bool_t hb_set_previous(const hb_set_t* set, hb_codepoint_t* codepoint) + hb_bool_t hb_set_next_range(const hb_set_t* set, hb_codepoint_t* first, hb_codepoint_t* last) + hb_bool_t hb_set_previous_range(const hb_set_t* set, hb_codepoint_t* first, hb_codepoint_t* last) + unsigned int hb_set_next_many(const hb_set_t* set, hb_codepoint_t codepoint, hb_codepoint_t* out, unsigned int size) cdef extern from "hb-ot.h": @@ -660,7 +660,7 @@ cdef extern from "hb-subset-repacker.h": unsigned int num_virtual_links hb_link_t *virtual_links - hb_blob_t* hb_subset_repack_or_fail ( + hb_blob_t* hb_subset_repack_or_fail( hb_tag_t table_tag, hb_object_t* hb_objects, unsigned int num_hb_objs) From 007c21fa7aae9b4338557f860caf1d30d23f9a4b Mon Sep 17 00:00:00 2001 From: Khaled Hosny Date: Mon, 25 Dec 2023 17:01:00 +0200 Subject: [PATCH 2/6] Add hb-ot-math definitions --- src/uharfbuzz/charfbuzz.pxd | 137 ++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/src/uharfbuzz/charfbuzz.pxd b/src/uharfbuzz/charfbuzz.pxd index b7c6680..29d073c 100644 --- a/src/uharfbuzz/charfbuzz.pxd +++ b/src/uharfbuzz/charfbuzz.pxd @@ -647,6 +647,143 @@ cdef extern from "hb-ot.h": # hb-ot-font.h void hb_ot_font_set_funcs(hb_font_t* font) + # hb-ot-math.h + ctypedef enum hb_ot_math_constant_t: + HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN + HB_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN + HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT + HB_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT + HB_OT_MATH_CONSTANT_MATH_LEADING + HB_OT_MATH_CONSTANT_AXIS_HEIGHT + HB_OT_MATH_CONSTANT_ACCENT_BASE_HEIGHT + HB_OT_MATH_CONSTANT_FLATTENED_ACCENT_BASE_HEIGHT + HB_OT_MATH_CONSTANT_SUBSCRIPT_SHIFT_DOWN + HB_OT_MATH_CONSTANT_SUBSCRIPT_TOP_MAX + HB_OT_MATH_CONSTANT_SUBSCRIPT_BASELINE_DROP_MIN + HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP + HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP_CRAMPED + HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MIN + HB_OT_MATH_CONSTANT_SUPERSCRIPT_BASELINE_DROP_MAX + HB_OT_MATH_CONSTANT_SUB_SUPERSCRIPT_GAP_MIN + HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MAX_WITH_SUBSCRIPT + HB_OT_MATH_CONSTANT_SPACE_AFTER_SCRIPT + HB_OT_MATH_CONSTANT_UPPER_LIMIT_GAP_MIN + HB_OT_MATH_CONSTANT_UPPER_LIMIT_BASELINE_RISE_MIN + HB_OT_MATH_CONSTANT_LOWER_LIMIT_GAP_MIN + HB_OT_MATH_CONSTANT_LOWER_LIMIT_BASELINE_DROP_MIN + HB_OT_MATH_CONSTANT_STACK_TOP_SHIFT_UP + HB_OT_MATH_CONSTANT_STACK_TOP_DISPLAY_STYLE_SHIFT_UP + HB_OT_MATH_CONSTANT_STACK_BOTTOM_SHIFT_DOWN + HB_OT_MATH_CONSTANT_STACK_BOTTOM_DISPLAY_STYLE_SHIFT_DOWN + HB_OT_MATH_CONSTANT_STACK_GAP_MIN + HB_OT_MATH_CONSTANT_STACK_DISPLAY_STYLE_GAP_MIN + HB_OT_MATH_CONSTANT_STRETCH_STACK_TOP_SHIFT_UP + HB_OT_MATH_CONSTANT_STRETCH_STACK_BOTTOM_SHIFT_DOWN + HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_ABOVE_MIN + HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_BELOW_MIN + HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_SHIFT_UP + HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_DISPLAY_STYLE_SHIFT_UP + HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_SHIFT_DOWN + HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_DISPLAY_STYLE_SHIFT_DOWN + HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_GAP_MIN + HB_OT_MATH_CONSTANT_FRACTION_NUM_DISPLAY_STYLE_GAP_MIN + HB_OT_MATH_CONSTANT_FRACTION_RULE_THICKNESS + HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_GAP_MIN + HB_OT_MATH_CONSTANT_FRACTION_DENOM_DISPLAY_STYLE_GAP_MIN + HB_OT_MATH_CONSTANT_SKEWED_FRACTION_HORIZONTAL_GAP + HB_OT_MATH_CONSTANT_SKEWED_FRACTION_VERTICAL_GAP + HB_OT_MATH_CONSTANT_OVERBAR_VERTICAL_GAP + HB_OT_MATH_CONSTANT_OVERBAR_RULE_THICKNESS + HB_OT_MATH_CONSTANT_OVERBAR_EXTRA_ASCENDER + HB_OT_MATH_CONSTANT_UNDERBAR_VERTICAL_GAP + HB_OT_MATH_CONSTANT_UNDERBAR_RULE_THICKNESS + HB_OT_MATH_CONSTANT_UNDERBAR_EXTRA_DESCENDER + HB_OT_MATH_CONSTANT_RADICAL_VERTICAL_GAP + HB_OT_MATH_CONSTANT_RADICAL_DISPLAY_STYLE_VERTICAL_GAP + HB_OT_MATH_CONSTANT_RADICAL_RULE_THICKNESS + HB_OT_MATH_CONSTANT_RADICAL_EXTRA_ASCENDER + HB_OT_MATH_CONSTANT_RADICAL_KERN_BEFORE_DEGREE + HB_OT_MATH_CONSTANT_RADICAL_KERN_AFTER_DEGREE + HB_OT_MATH_CONSTANT_RADICAL_DEGREE_BOTTOM_RAISE_PERCENT + + ctypedef enum hb_ot_math_kern_t: + HB_OT_MATH_KERN_TOP_RIGHT + HB_OT_MATH_KERN_TOP_LEFT + HB_OT_MATH_KERN_BOTTOM_RIGHT + HB_OT_MATH_KERN_BOTTOM_LEFT + + ctypedef enum hb_ot_math_glyph_part_flags_t: + HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER + + + ctypedef struct hb_ot_math_kern_entry_t: + hb_position_t max_correction_height + hb_position_t kern_value + + ctypedef struct hb_ot_math_glyph_variant_t: + hb_codepoint_t glyph + hb_position_t advance + + ctypedef 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_bool_t hb_ot_math_has_data(hb_face_t *face) + + hb_position_t hb_ot_math_get_constant( + hb_font_t *font, + hb_ot_math_constant_t constant) + + hb_position_t hb_ot_math_get_glyph_italics_correction( + hb_font_t *font, + hb_codepoint_t glyph) + + hb_position_t hb_ot_math_get_glyph_top_accent_attachment( + hb_font_t *font, + hb_codepoint_t glyph) + + hb_bool_t hb_ot_math_is_glyph_extended_shape( + hb_face_t *face, + hb_codepoint_t glyph) + + 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) + + unsigned int hb_ot_math_get_glyph_kernings( + hb_font_t *font, + 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 + + 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_position_t hb_ot_math_get_min_connector_overlap( + hb_font_t *font, + hb_direction_t direction) + + 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 + cdef extern from "hb-subset-repacker.h": ctypedef struct hb_link_t: unsigned int width From 5fd8a2b7d520857fb7e0dd5f3a732aee7e585b61 Mon Sep 17 00:00:00 2001 From: Khaled Hosny Date: Mon, 25 Dec 2023 17:51:14 +0200 Subject: [PATCH 3/6] Bind hb-ot-math API --- src/uharfbuzz/_harfbuzz.pyx | 160 ++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) diff --git a/src/uharfbuzz/_harfbuzz.pyx b/src/uharfbuzz/_harfbuzz.pyx index 14e539f..19c924a 100644 --- a/src/uharfbuzz/_harfbuzz.pyx +++ b/src/uharfbuzz/_harfbuzz.pyx @@ -1266,6 +1266,166 @@ def ot_layout_get_baseline(font: Font, else: return None +def ot_math_has_data(face: Face) -> bool: + return hb_ot_math_has_data(face._hb_face) + +class OTMathConstant(IntEnum): + SCRIPT_PERCENT_SCALE_DOWN = HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN + SCRIPT_SCRIPT_PERCENT_SCALE_DOWN = HB_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN + DELIMITED_SUB_FORMULA_MIN_HEIGHT = HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT + DISPLAY_OPERATOR_MIN_HEIGHT = HB_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT + MATH_LEADING = HB_OT_MATH_CONSTANT_MATH_LEADING + AXIS_HEIGHT = HB_OT_MATH_CONSTANT_AXIS_HEIGHT + ACCENT_BASE_HEIGHT = HB_OT_MATH_CONSTANT_ACCENT_BASE_HEIGHT + FLATTENED_ACCENT_BASE_HEIGHT = HB_OT_MATH_CONSTANT_FLATTENED_ACCENT_BASE_HEIGHT + SUBSCRIPT_SHIFT_DOWN = HB_OT_MATH_CONSTANT_SUBSCRIPT_SHIFT_DOWN + SUBSCRIPT_TOP_MAX = HB_OT_MATH_CONSTANT_SUBSCRIPT_TOP_MAX + SUBSCRIPT_BASELINE_DROP_MIN = HB_OT_MATH_CONSTANT_SUBSCRIPT_BASELINE_DROP_MIN + SUPERSCRIPT_SHIFT_UP = HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP + SUPERSCRIPT_SHIFT_UP_CRAMPED = HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP_CRAMPED + SUPERSCRIPT_BOTTOM_MIN = HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MIN + SUPERSCRIPT_BASELINE_DROP_MAX = HB_OT_MATH_CONSTANT_SUPERSCRIPT_BASELINE_DROP_MAX + SUB_SUPERSCRIPT_GAP_MIN = HB_OT_MATH_CONSTANT_SUB_SUPERSCRIPT_GAP_MIN + SUPERSCRIPT_BOTTOM_MAX_WITH_SUBSCRIPT = HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MAX_WITH_SUBSCRIPT + SPACE_AFTER_SCRIPT = HB_OT_MATH_CONSTANT_SPACE_AFTER_SCRIPT + UPPER_LIMIT_GAP_MIN = HB_OT_MATH_CONSTANT_UPPER_LIMIT_GAP_MIN + UPPER_LIMIT_BASELINE_RISE_MIN = HB_OT_MATH_CONSTANT_UPPER_LIMIT_BASELINE_RISE_MIN + LOWER_LIMIT_GAP_MIN = HB_OT_MATH_CONSTANT_LOWER_LIMIT_GAP_MIN + LOWER_LIMIT_BASELINE_DROP_MIN = HB_OT_MATH_CONSTANT_LOWER_LIMIT_BASELINE_DROP_MIN + STACK_TOP_SHIFT_UP = HB_OT_MATH_CONSTANT_STACK_TOP_SHIFT_UP + STACK_TOP_DISPLAY_STYLE_SHIFT_UP = HB_OT_MATH_CONSTANT_STACK_TOP_DISPLAY_STYLE_SHIFT_UP + STACK_BOTTOM_SHIFT_DOWN = HB_OT_MATH_CONSTANT_STACK_BOTTOM_SHIFT_DOWN + STACK_BOTTOM_DISPLAY_STYLE_SHIFT_DOWN = HB_OT_MATH_CONSTANT_STACK_BOTTOM_DISPLAY_STYLE_SHIFT_DOWN + STACK_GAP_MIN = HB_OT_MATH_CONSTANT_STACK_GAP_MIN + STACK_DISPLAY_STYLE_GAP_MIN = HB_OT_MATH_CONSTANT_STACK_DISPLAY_STYLE_GAP_MIN + STRETCH_STACK_TOP_SHIFT_UP = HB_OT_MATH_CONSTANT_STRETCH_STACK_TOP_SHIFT_UP + STRETCH_STACK_BOTTOM_SHIFT_DOWN = HB_OT_MATH_CONSTANT_STRETCH_STACK_BOTTOM_SHIFT_DOWN + STRETCH_STACK_GAP_ABOVE_MIN = HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_ABOVE_MIN + STRETCH_STACK_GAP_BELOW_MIN = HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_BELOW_MIN + FRACTION_NUMERATOR_SHIFT_UP = HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_SHIFT_UP + FRACTION_NUMERATOR_DISPLAY_STYLE_SHIFT_UP = HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_DISPLAY_STYLE_SHIFT_UP + FRACTION_DENOMINATOR_SHIFT_DOWN = HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_SHIFT_DOWN + FRACTION_DENOMINATOR_DISPLAY_STYLE_SHIFT_DOWN = HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_DISPLAY_STYLE_SHIFT_DOWN + FRACTION_NUMERATOR_GAP_MIN = HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_GAP_MIN + FRACTION_NUM_DISPLAY_STYLE_GAP_MIN = HB_OT_MATH_CONSTANT_FRACTION_NUM_DISPLAY_STYLE_GAP_MIN + FRACTION_RULE_THICKNESS = HB_OT_MATH_CONSTANT_FRACTION_RULE_THICKNESS + FRACTION_DENOMINATOR_GAP_MIN = HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_GAP_MIN + FRACTION_DENOM_DISPLAY_STYLE_GAP_MIN = HB_OT_MATH_CONSTANT_FRACTION_DENOM_DISPLAY_STYLE_GAP_MIN + SKEWED_FRACTION_HORIZONTAL_GAP = HB_OT_MATH_CONSTANT_SKEWED_FRACTION_HORIZONTAL_GAP + SKEWED_FRACTION_VERTICAL_GAP = HB_OT_MATH_CONSTANT_SKEWED_FRACTION_VERTICAL_GAP + OVERBAR_VERTICAL_GAP = HB_OT_MATH_CONSTANT_OVERBAR_VERTICAL_GAP + OVERBAR_RULE_THICKNESS = HB_OT_MATH_CONSTANT_OVERBAR_RULE_THICKNESS + OVERBAR_EXTRA_ASCENDER = HB_OT_MATH_CONSTANT_OVERBAR_EXTRA_ASCENDER + UNDERBAR_VERTICAL_GAP = HB_OT_MATH_CONSTANT_UNDERBAR_VERTICAL_GAP + UNDERBAR_RULE_THICKNESS = HB_OT_MATH_CONSTANT_UNDERBAR_RULE_THICKNESS + UNDERBAR_EXTRA_DESCENDER = HB_OT_MATH_CONSTANT_UNDERBAR_EXTRA_DESCENDER + RADICAL_VERTICAL_GAP = HB_OT_MATH_CONSTANT_RADICAL_VERTICAL_GAP + RADICAL_DISPLAY_STYLE_VERTICAL_GAP = HB_OT_MATH_CONSTANT_RADICAL_DISPLAY_STYLE_VERTICAL_GAP + RADICAL_RULE_THICKNESS = HB_OT_MATH_CONSTANT_RADICAL_RULE_THICKNESS + RADICAL_EXTRA_ASCENDER = HB_OT_MATH_CONSTANT_RADICAL_EXTRA_ASCENDER + RADICAL_KERN_BEFORE_DEGREE = HB_OT_MATH_CONSTANT_RADICAL_KERN_BEFORE_DEGREE + RADICAL_KERN_AFTER_DEGREE = HB_OT_MATH_CONSTANT_RADICAL_KERN_AFTER_DEGREE + RADICAL_DEGREE_BOTTOM_RAISE_PERCENT = HB_OT_MATH_CONSTANT_RADICAL_DEGREE_BOTTOM_RAISE_PERCENT + +def ot_math_get_constant(font: Font, constant: OTMathConstant) -> int: + return hb_ot_math_get_constant(font._hb_font, constant) + +def ot_math_get_glyph_italics_correction(font: Font, glyph: int) -> int: + return hb_ot_math_get_glyph_italics_correction(font._hb_font, glyph) + +def ot_math_get_glyph_top_accent_attachment(font: Font, glyph: int) -> int: + return hb_ot_math_get_glyph_top_accent_attachment(font._hb_font, glyph) + +def ot_math_is_glyph_extended_shape(face: Face, glyph: int) -> bool: + return hb_ot_math_is_glyph_extended_shape(face._hb_face, glyph) + +def ot_math_get_min_connector_overlap(font: Font, direction: str) -> int: + cdef bytes packed = direction.encode() + cdef char* cstr = packed + cdef hb_direction_t hb_direction = hb_direction_from_string(cstr, -1) + return hb_ot_math_get_min_connector_overlap(font._hb_font, hb_direction) + +class OTMathKern(IntEnum): + TOP_RIGHT = HB_OT_MATH_KERN_TOP_RIGHT + TOP_LEFT = HB_OT_MATH_KERN_TOP_LEFT + BOTTOM_RIGHT = HB_OT_MATH_KERN_BOTTOM_RIGHT + BOTTOM_LEFT = HB_OT_MATH_KERN_BOTTOM_LEFT + +def ot_math_get_glyph_kerning(font: Font, + glyph: int, + kern: OTMathKern, + int correction_height) -> int: + return hb_ot_math_get_glyph_kerning(font._hb_font, glyph, kern, correction_height) + +OTMathKernEntry = namedtuple("OTMathKernEntry", ["max_correction_height", "kern_value"]) + +def ot_math_get_glyph_kernings(font: Font, + glyph: int, + kern: OTMathKern) -> List[OTMathKernEntry]: + cdef unsigned int count = STATIC_TAGS_ARRAY_SIZE + cdef hb_ot_math_kern_entry_t kerns_array[STATIC_TAGS_ARRAY_SIZE] + cdef list kerns = [] + cdef unsigned int i + cdef unsigned int start_offset = 0 + while count == STATIC_TAGS_ARRAY_SIZE: + hb_ot_math_get_glyph_kernings(font._hb_font, glyph, kern, start_offset, + &count, kerns_array) + for i in range(count): + kerns.append(OTMathKernEntry(kerns_array[i].max_correction_height, kerns_array[i].kern_value)) + start_offset += count + return kerns + +OTMathGlyphVariant = namedtuple("OTMathGlyphVariant", ["glyph", "advance"]) + +def ot_math_get_glyph_variants(font: Font, glyph: int, direction: str) -> List[OTMathGlyphVariants]: + cdef bytes packed = direction.encode() + cdef char* cstr = packed + cdef hb_direction_t hb_direction = hb_direction_from_string(cstr, -1) + cdef unsigned int count = STATIC_TAGS_ARRAY_SIZE + cdef hb_ot_math_glyph_variant_t variants_array[STATIC_TAGS_ARRAY_SIZE] + cdef list variants = [] + cdef unsigned int i + cdef unsigned int start_offset = 0 + while count == STATIC_TAGS_ARRAY_SIZE: + hb_ot_math_get_glyph_variants(font._hb_font, glyph, hb_direction, start_offset, + &count, variants_array) + for i in range(count): + variants.append(OTMathGlyphVariant(variants_array[i].glyph, variants_array[i].advance)) + start_offset += count + return variants + +class OTMathGlyphPartFlags(IntFlag): + EXTENDER = HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER + +OTMathGlyphPart = namedtuple( + "OTMathGlyphPart", + ["glyph", "start_connector_length", "end_connector_length", "full_advance", "flags"] +) + +def ot_math_get_glyph_assembly(font: Font, + glyph: int, + direction: str) -> Tuple[List[OTMathGlyphPart], int]: + cdef bytes packed = direction.encode() + cdef char* cstr = packed + cdef hb_direction_t hb_direction = hb_direction_from_string(cstr, -1) + cdef unsigned int count = STATIC_TAGS_ARRAY_SIZE + cdef hb_ot_math_glyph_part_t assembly_array[STATIC_TAGS_ARRAY_SIZE] + cdef list assembly = [] + cdef unsigned int i + cdef unsigned int start_offset = 0 + cdef hb_position_t italics_correction = 0 + while count == STATIC_TAGS_ARRAY_SIZE: + hb_ot_math_get_glyph_assembly(font._hb_font, + glyph, hb_direction, start_offset, + &count, assembly_array, &italics_correction) + for i in range(count): + assembly.append( + OTMathGlyphPart(assembly_array[i].glyph, assembly_array[i].start_connector_length, + assembly_array[i].end_connector_length, assembly_array[i].full_advance, + OTMathGlyphPartFlags(assembly_array[i].flags))) + start_offset += count + return assembly, italics_correction + def ot_font_set_funcs(Font font): hb_ot_font_set_funcs(font._hb_font) From 995932001054c13054663b431b86014e70dded85 Mon Sep 17 00:00:00 2001 From: Khaled Hosny Date: Tue, 26 Dec 2023 16:18:18 +0200 Subject: [PATCH 4/6] Some error handling --- src/uharfbuzz/_harfbuzz.pyx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/uharfbuzz/_harfbuzz.pyx b/src/uharfbuzz/_harfbuzz.pyx index 19c924a..43fd13c 100644 --- a/src/uharfbuzz/_harfbuzz.pyx +++ b/src/uharfbuzz/_harfbuzz.pyx @@ -1328,6 +1328,8 @@ class OTMathConstant(IntEnum): RADICAL_DEGREE_BOTTOM_RAISE_PERCENT = HB_OT_MATH_CONSTANT_RADICAL_DEGREE_BOTTOM_RAISE_PERCENT def ot_math_get_constant(font: Font, constant: OTMathConstant) -> int: + if constant >= len(OTMathConstant): + raise ValueError("invalid constant") return hb_ot_math_get_constant(font._hb_font, constant) def ot_math_get_glyph_italics_correction(font: Font, glyph: int) -> int: @@ -1355,6 +1357,8 @@ def ot_math_get_glyph_kerning(font: Font, glyph: int, kern: OTMathKern, int correction_height) -> int: + if kern >= len(OTMathKern): + raise ValueError("invalid kern") return hb_ot_math_get_glyph_kerning(font._hb_font, glyph, kern, correction_height) OTMathKernEntry = namedtuple("OTMathKernEntry", ["max_correction_height", "kern_value"]) @@ -1362,6 +1366,8 @@ OTMathKernEntry = namedtuple("OTMathKernEntry", ["max_correction_height", "kern_ def ot_math_get_glyph_kernings(font: Font, glyph: int, kern: OTMathKern) -> List[OTMathKernEntry]: + if kern >= len(OTMathKern): + raise ValueError("invalid kern") cdef unsigned int count = STATIC_TAGS_ARRAY_SIZE cdef hb_ot_math_kern_entry_t kerns_array[STATIC_TAGS_ARRAY_SIZE] cdef list kerns = [] From fa6d1b7d03a50e7bfb039289a70836c3c0049e88 Mon Sep 17 00:00:00 2001 From: Khaled Hosny Date: Tue, 26 Dec 2023 15:01:07 +0200 Subject: [PATCH 5/6] Black --- tests/test_uharfbuzz.py | 370 +++++++++++++++++++++++++++------------- 1 file changed, 247 insertions(+), 123 deletions(-) diff --git a/tests/test_uharfbuzz.py b/tests/test_uharfbuzz.py index ad56956..61eae51 100644 --- a/tests/test_uharfbuzz.py +++ b/tests/test_uharfbuzz.py @@ -201,17 +201,35 @@ def test_from_file_path_fail(self): class TestFace: - def test_properties(self, blankfont): - face = blankfont.face assert face.index == 0 assert face.upem == 1000 assert face.glyph_count == 9 - assert face.table_tags == ['BASE', 'GPOS', 'GSUB', 'OS/2', 'cmap', 'cvt ', 'fpgm', 'gasp', 'glyf', 'head', 'hhea', 'hmtx', 'loca', 'maxp', 'name', 'post', 'prep'] + assert face.table_tags == [ + "BASE", + "GPOS", + "GSUB", + "OS/2", + "cmap", + "cvt ", + "fpgm", + "gasp", + "glyf", + "head", + "hhea", + "hmtx", + "loca", + "maxp", + "name", + "post", + "prep", + ] - assert face.unicodes == hb.Set({0x61, 0x62, 0x63, 0x64, 0x65, 0xe7, 0x431, 0x1f4a9}) + assert face.unicodes == hb.Set( + {0x61, 0x62, 0x63, 0x64, 0x65, 0xE7, 0x431, 0x1F4A9} + ) assert face.variation_selectors == hb.Set() assert face.variation_unicodes(1) == hb.Set() @@ -228,12 +246,12 @@ def test_get_glyph_extents(self, opensans): assert opensans.get_glyph_extents(1000) is None def test_get_font_extents(self, blankfont): - extents = blankfont.get_font_extents('ltr') + extents = blankfont.get_font_extents("ltr") assert (880, -120, 0) == extents assert 880 == extents.ascender assert -120 == extents.descender assert 0 == extents.line_gap - extents = blankfont.get_font_extents('ttb') + extents = blankfont.get_font_extents("ttb") assert (500, -500, 0) == extents assert 500 == extents.ascender assert -500 == extents.descender @@ -279,7 +297,6 @@ def test_set_var_coords_normalized(self, mutatorsans): mutatorsans.set_var_coords_normalized(["a"]) def test_properties(self, blankfont): - assert blankfont.scale == (1000, 1000) blankfont.scale = (1024, 1024) assert blankfont.scale == (1024, 1024) @@ -289,12 +306,12 @@ def test_properties(self, blankfont): assert blankfont.ppem == (16, 24) assert blankfont.ptem == 0 - blankfont.ptem = 12. - assert blankfont.ptem == 12. + blankfont.ptem = 12.0 + assert blankfont.ptem == 12.0 assert blankfont.synthetic_slant == 0 - blankfont.synthetic_slant = .2 - assert blankfont.synthetic_slant == pytest.approx(.2) + blankfont.synthetic_slant = 0.2 + assert blankfont.synthetic_slant == pytest.approx(0.2) class TestShape: @@ -323,7 +340,13 @@ def test_shape_set_shaper(self, blankfont): buf.guess_segment_properties() hb.shape(blankfont, buf, shapers=["fallback"]) pos = [g.position for g in buf.glyph_positions] - expected = [(0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0)] + expected = [ + (0, 0, 0, 0), + (0, 0, 0, 0), + (0, 0, 0, 0), + (0, 0, 0, 0), + (0, 0, 0, 0), + ] assert pos == expected @pytest.mark.skipif(sys.platform != "win32", reason="requires Windows") @@ -334,7 +357,13 @@ def test_shape_set_shaper_directwrite(self, blankfont): buf.guess_segment_properties() hb.shape(blankfont, buf, shapers=["directwrite"]) pos = [g.position for g in buf.glyph_positions] - expected = [(0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0)] + expected = [ + (0, 0, 0, 0), + (0, 0, 0, 0), + (0, 0, 0, 0), + (0, 0, 0, 0), + (0, 0, 0, 0), + ] assert pos == expected @pytest.mark.xfail @@ -346,7 +375,13 @@ def test_shape_set_shaper_uniscribe(self, blankfont): buf.guess_segment_properties() hb.shape(blankfont, buf, shapers=["uniscribe"]) pos = [g.position for g in buf.glyph_positions] - expected = [(0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0)] + expected = [ + (0, 0, 0, 0), + (0, 0, 0, 0), + (0, 0, 0, 0), + (0, 0, 0, 0), + (0, 0, 0, 0), + ] assert pos == expected @pytest.mark.skipif(sys.platform != "darwin", reason="requires macOS") @@ -357,7 +392,13 @@ def test_shape_set_shaper_coretext(self, blankfont): buf.guess_segment_properties() hb.shape(blankfont, buf, shapers=["coretext"]) pos = [g.position for g in buf.glyph_positions] - expected = [(0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0)] + expected = [ + (0, 0, 0, 0), + (0, 0, 0, 0), + (0, 0, 0, 0), + (0, 0, 0, 0), + (0, 0, 0, 0), + ] assert pos == expected @pytest.mark.parametrize( @@ -384,16 +425,28 @@ def test_glyh_name_no_features(self, blankfont, string, expected): # or if the glyph ID does not exist. glyph_names = [blankfont.glyph_to_string(g.codepoint) for g in buf.glyph_infos] assert glyph_names == expected - assert blankfont.glyph_to_string(1000) == 'gid1000' + assert blankfont.glyph_to_string(1000) == "gid1000" @pytest.mark.parametrize( "string, features, expected", [ # The calt feature replaces c by a in the context e, d, c', b, a. ("edcbaedcba", {}, ["e", "d", "a", "b", "a", "e", "d", "a", "b", "a"]), - ("edcbaedcba", {"calt[2]": False}, ["e", "d", "c", "b", "a", "e", "d", "a", "b", "a"]), - ("edcbaedcba", {"calt": [(7, 8, False)]}, ["e", "d", "a", "b", "a", "e", "d", "c", "b", "a"]), - ("edcbaedcba", {"calt": [(0, 10, False), (7, 8, True)]}, ["e", "d", "c", "b", "a", "e", "d", "a", "b", "a"]), + ( + "edcbaedcba", + {"calt[2]": False}, + ["e", "d", "c", "b", "a", "e", "d", "a", "b", "a"], + ), + ( + "edcbaedcba", + {"calt": [(7, 8, False)]}, + ["e", "d", "a", "b", "a", "e", "d", "c", "b", "a"], + ), + ( + "edcbaedcba", + {"calt": [(0, 10, False), (7, 8, True)]}, + ["e", "d", "c", "b", "a", "e", "d", "a", "b", "a"], + ), ], ) def test_features_slice(self, blankfont, string, features, expected): @@ -463,7 +516,9 @@ def v_origin_func(font, gid, data): blankfont.funcs = funcs hb.shape(blankfont, buf) - infos = [(pos.y_advance, pos.x_offset, pos.y_offset) for pos in buf.glyph_positions] + infos = [ + (pos.y_advance, pos.x_offset, pos.y_offset) for pos in buf.glyph_positions + ] assert infos == expected def test_font_extents_funcs(self, blankfont): @@ -477,8 +532,8 @@ def font_v_extents_func(font, data): funcs.set_font_h_extents_func(font_h_extents_func) funcs.set_font_v_extents_func(font_v_extents_func) blankfont.funcs = funcs - assert (123, -456, 789) == blankfont.get_font_extents('ltr') - assert (987, -654, 321) == blankfont.get_font_extents('ttb') + assert (123, -456, 789) == blankfont.get_font_extents("ltr") + assert (987, -654, 321) == blankfont.get_font_extents("ttb") def test_message_func(self, blankfont): # Glyph IDs 1, 2, 3, 4, 5 map to glyphs a, b, c, d, e. @@ -507,31 +562,49 @@ def message(msg): expected_messages = [ "start table GSUB script tag 'DFLT'", "start lookup 0 feature 'calt'", - 'recursing to lookup 1 at 2', - 'replacing glyph at 2 (single substitution)', - 'replaced glyph at 2 (single substitution)', - 'recursed to lookup 1', + "recursing to lookup 1 at 2", + "replacing glyph at 2 (single substitution)", + "replaced glyph at 2 (single substitution)", + "recursed to lookup 1", "end lookup 0 feature 'calt'", "end table GSUB script tag 'DFLT'", "start table GPOS script tag 'DFLT'", "start lookup 0 feature 'kern'", - 'try kerning glyphs at 3,4', - 'kerned glyphs at 3,4', - 'tried kerning glyphs at 3,4', + "try kerning glyphs at 3,4", + "kerned glyphs at 3,4", + "tried kerning glyphs at 3,4", "end lookup 0 feature 'kern'", "end table GPOS script tag 'DFLT'", ] assert messages == expected_messages gids_trace = [[g.codepoint for g in infos] for infos in infos_trace] - assert gids_trace == [[5, 4, 3, 2, 1], [5, 4, 3, 2, 1], [5, 4, 3, 2, 1], - [5, 4, 3, 2, 1], [5, 4, 1, 2, 1], [5, 4, 1, 2, 1], - [5, 4, 1, 2, 1], [5, 4, 1, 2, 1], [5, 4, 1, 2, 1], - [5, 4, 1, 2, 1], [5, 4, 1, 2, 1], [5, 4, 1, 2, 1], - [5, 4, 1, 2, 1], [5, 4, 1, 2, 1], [5, 4, 1, 2, 1]] + assert gids_trace == [ + [5, 4, 3, 2, 1], + [5, 4, 3, 2, 1], + [5, 4, 3, 2, 1], + [5, 4, 3, 2, 1], + [5, 4, 1, 2, 1], + [5, 4, 1, 2, 1], + [5, 4, 1, 2, 1], + [5, 4, 1, 2, 1], + [5, 4, 1, 2, 1], + [5, 4, 1, 2, 1], + [5, 4, 1, 2, 1], + [5, 4, 1, 2, 1], + [5, 4, 1, 2, 1], + [5, 4, 1, 2, 1], + [5, 4, 1, 2, 1], + ] advances_trace = [[g.x_advance for g in pos] for pos in positions_trace if pos] - assert advances_trace == [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], - [0, 0, 0, 100, 0], [0, 0, 0, 100, 0], [0, 0, 0, 100, 0], - [0, 0, 0, 100, 0]] + assert advances_trace == [ + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 100, 0], + [0, 0, 0, 100, 0], + [0, 0, 0, 100, 0], + [0, 0, 0, 100, 0], + ] def test_message_func_return_false(self, blankfont): # Glyph IDs 1, 2, 3, 4, 5 map to glyphs a, b, c, d, e. @@ -580,14 +653,19 @@ def test_message_func_crash(self, blankfont): def test_draw_funcs(self, opensans): funcs = hb.DrawFuncs() container = [] - def move_to(x,y,c): + + def move_to(x, y, c): c.append(f"M{x:g},{y:g}") - def line_to(x,y,c): + + def line_to(x, y, c): c.append(f"L{x:g},{y:g}") - def cubic_to(c1x,c1y,c2x,c2y,x,y,c): + + def cubic_to(c1x, c1y, c2x, c2y, x, y, c): c.append(f"C{c1x:g},{c1y:g} {c2x:g},{c2y:g} {x:g},{y:g}") - def quadratic_to(c1x,c1y,x,y,c): + + def quadratic_to(c1x, c1y, x, y, c): c.append(f"Q{c1x:g},{c1y:g} {x:g},{y:g}") + def close_path(c): c.append("Z") @@ -597,19 +675,27 @@ def close_path(c): funcs.set_quadratic_to_func(quadratic_to, container) funcs.set_close_path_func(close_path, container) opensans.draw_glyph(funcs, 1) - assert "".join(container) == "M1120,0L938,465L352,465L172,0L0,0L578,1468L721,1468L1296,0L1120,0ZM885,618L715,1071Q682,1157 647,1282Q625,1186 584,1071L412,618L885,618Z" + assert ( + "".join(container) + == "M1120,0L938,465L352,465L172,0L0,0L578,1468L721,1468L1296,0L1120,0ZM885,618L715,1071Q682,1157 647,1282Q625,1186 584,1071L412,618L885,618Z" + ) def test_draw_funcs(self, opensans): funcs = hb.DrawFuncs() container = [] - def move_to(x,y,c): + + def move_to(x, y, c): c.append(f"M{x:g},{y:g}") - def line_to(x,y,c): + + def line_to(x, y, c): c.append(f"L{x:g},{y:g}") - def cubic_to(c1x,c1y,c2x,c2y,x,y,c): + + def cubic_to(c1x, c1y, c2x, c2y, x, y, c): c.append(f"C{c1x:g},{c1y:g} {c2x:g},{c2y:g} {x:g},{y:g}") - def quadratic_to(c1x,c1y,x,y,c): + + def quadratic_to(c1x, c1y, x, y, c): c.append(f"Q{c1x:g},{c1y:g} {x:g},{y:g}") + def close_path(c): c.append("Z") @@ -619,9 +705,15 @@ def close_path(c): funcs.set_quadratic_to_func(quadratic_to) funcs.set_close_path_func(close_path) opensans.draw_glyph(1, funcs, container) - assert "".join(container) == "M1120,0L938,465L352,465L172,0L0,0L578,1468L721,1468L1296,0L1120,0ZM885,618L715,1071Q682,1157 647,1282Q625,1186 584,1071L412,618L885,618Z" - - @pytest.mark.xfail(platform.python_implementation() == "PyPy", reason="PyPy's ctypes has no 'pythonapi' attribute") + assert ( + "".join(container) + == "M1120,0L938,465L352,465L172,0L0,0L578,1468L721,1468L1296,0L1120,0ZM885,618L715,1071Q682,1157 647,1282Q625,1186 584,1071L412,618L885,618Z" + ) + + @pytest.mark.xfail( + platform.python_implementation() == "PyPy", + reason="PyPy's ctypes has no 'pythonapi' attribute", + ) def test_draw_funcs_pycapsule(self, opensans): import ctypes import uharfbuzz._harfbuzz_test @@ -646,25 +738,52 @@ def cap(x): funcs.set_close_path_func(cap(lib._test_close_path)) opensans.draw_glyph(1, funcs, container_cap) - assert container.value == b"M1120,0L938,465L352,465L172,0L0,0L578,1468L721,1468L1296,0L1120,0ZM885,618L715,1071Q682,1157 647,1282Q625,1186 584,1071L412,618L885,618Z" + assert ( + container.value + == b"M1120,0L938,465L352,465L172,0L0,0L578,1468L721,1468L1296,0L1120,0ZM885,618L715,1071Q682,1157 647,1282Q625,1186 584,1071L412,618L885,618Z" + ) def test_draw_pen(self, opensans): class TestPen: def __init__(self): self.value = [] + def moveTo(self, p0): - self.value.append(('moveTo', (p0,))) + self.value.append(("moveTo", (p0,))) + def lineTo(self, p1): - self.value.append(('lineTo', (p1,))) + self.value.append(("lineTo", (p1,))) + def qCurveTo(self, *points): - self.value.append(('qCurveTo', points)) + self.value.append(("qCurveTo", points)) + def curveTo(self, *points): - self.value.append(('curveTo', points)) + self.value.append(("curveTo", points)) + def closePath(self): - self.value.append(('closePath', ())) + self.value.append(("closePath", ())) + pen = TestPen() opensans.draw_glyph_with_pen(1, pen) - assert pen.value == [('moveTo', ((1120, 0),)), ('lineTo', ((938, 465),)), ('lineTo', ((352, 465),)), ('lineTo', ((172, 0),)), ('lineTo', ((0, 0),)), ('lineTo', ((578, 1468),)), ('lineTo', ((721, 1468),)), ('lineTo', ((1296, 0),)), ('lineTo', ((1120, 0),)), ('closePath', ()), ('moveTo', ((885, 618),)), ('lineTo', ((715, 1071),)), ('qCurveTo', ((682, 1157), (647, 1282))), ('qCurveTo', ((625, 1186), (584, 1071))), ('lineTo', ((412, 618),)), ('lineTo', ((885, 618),)), ('closePath', ())] + assert pen.value == [ + ("moveTo", ((1120, 0),)), + ("lineTo", ((938, 465),)), + ("lineTo", ((352, 465),)), + ("lineTo", ((172, 0),)), + ("lineTo", ((0, 0),)), + ("lineTo", ((578, 1468),)), + ("lineTo", ((721, 1468),)), + ("lineTo", ((1296, 0),)), + ("lineTo", ((1120, 0),)), + ("closePath", ()), + ("moveTo", ((885, 618),)), + ("lineTo", ((715, 1071),)), + ("qCurveTo", ((682, 1157), (647, 1282))), + ("qCurveTo", ((625, 1186), (584, 1071))), + ("lineTo", ((412, 618),)), + ("lineTo", ((885, 618),)), + ("closePath", ()), + ] class MessageCollector: @@ -683,42 +802,43 @@ def test_ot_layout_get_baseline_invalid_tag(self, blankfont): "baseline_tag, script_tag, direction, expected_value", [ ("icfb", "grek", "LTR", None), # BASE table doesn't contain grek script - ("icfb", "latn", "LTR", -70), ("icft", "latn", "LTR", 830), ("romn", "latn", "LTR", 0), ("ideo", "latn", "LTR", -120), - ("icfb", "kana", "LTR", -71), ("icft", "kana", "LTR", 831), ("romn", "kana", "LTR", 1), ("ideo", "kana", "LTR", -121), - ("icfb", "latn", "TTB", 50), ("icft", "latn", "TTB", 950), ("romn", "latn", "TTB", 120), ("ideo", "latn", "TTB", 0), - ("icfb", "kana", "TTB", 51), ("icft", "kana", "TTB", 951), ("romn", "kana", "TTB", 121), ("ideo", "kana", "TTB", 1), - ] + ], ) - def test_ot_layout_get_baseline(self, blankfont, baseline_tag, script_tag, direction, expected_value): - value = hb.ot_layout_get_baseline(blankfont, baseline_tag, direction, script_tag, "") + def test_ot_layout_get_baseline( + self, blankfont, baseline_tag, script_tag, direction, expected_value + ): + value = hb.ot_layout_get_baseline( + blankfont, baseline_tag, direction, script_tag, "" + ) assert value == expected_value + class TestGetTags: def test_ot_layout_language_get_feature_tags(self, blankfont): tags = hb.ot_layout_language_get_feature_tags(blankfont.face, "GPOS") - assert tags == ['kern'] + assert tags == ["kern"] tags = hb.ot_layout_language_get_feature_tags(blankfont.face, "GSUB") - assert tags == ['calt'] + assert tags == ["calt"] def test_ot_layout_table_get_script_tags(self, blankfont): tags = hb.ot_layout_table_get_script_tags(blankfont.face, "GPOS") - assert tags == ['DFLT'] + assert tags == ["DFLT"] def test_ot_layout_script_get_language_tags(self, blankfont): tags = hb.ot_layout_script_get_language_tags(blankfont.face, "GPOS", 0) @@ -741,49 +861,52 @@ def test_create_sub_font(): face = hb.Face(blob) font = hb.Font(face) font2 = hb.Font(font) - assert(font is not font2) - assert(font.face is font2.face) + assert font is not font2 + assert font.face is font2.face def test_harfbuzz_repacker(): table_data = [ - bytes(b'\x00\x00\xff\xff\x00\x01\x00\x00'), - bytes(b'\x00\x00\x00\x00'), - bytes(b'\x00\x01latn\x00\x00'), - bytes(b'\x00\x00\x00\x01\x00\x01'), - bytes(b'\x00\x01test\x00\x00'), - bytes(b'\x00\x01\x00\x01\x00\x02'), - bytes(b'\x00\x01\x00\x00\x00\x01'), - bytes(b'\x00\x01\x00\x00\x00\x01\x00\x00'), - bytes(b'\x00\x01\x00\x01\x00\x01'), - bytes(b'\x00\x02\x00\x01\x00\x02\x00\x01\x00\x00'), - bytes(b'\x00\x01\x00\x00'), - bytes(b'\x00\x01\x00\x00\x00\x01\x00\x00'), - bytes(b'\x00\x05\x00\x00\x00\x01\x00\x00'), - bytes(b'\x00\x02\x00\x00\x00\x00'), - bytes(b'\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00'), - ] + bytes(b"\x00\x00\xff\xff\x00\x01\x00\x00"), + bytes(b"\x00\x00\x00\x00"), + bytes(b"\x00\x01latn\x00\x00"), + bytes(b"\x00\x00\x00\x01\x00\x01"), + bytes(b"\x00\x01test\x00\x00"), + bytes(b"\x00\x01\x00\x01\x00\x02"), + bytes(b"\x00\x01\x00\x00\x00\x01"), + bytes(b"\x00\x01\x00\x00\x00\x01\x00\x00"), + bytes(b"\x00\x01\x00\x01\x00\x01"), + bytes(b"\x00\x02\x00\x01\x00\x02\x00\x01\x00\x00"), + bytes(b"\x00\x01\x00\x00"), + bytes(b"\x00\x01\x00\x00\x00\x01\x00\x00"), + bytes(b"\x00\x05\x00\x00\x00\x01\x00\x00"), + bytes(b"\x00\x02\x00\x00\x00\x00"), + bytes(b"\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00"), + ] obj_list = [ - ([], []), - ([(0,2,1)], []), - ([(6,2,2)], []), - ([], []), - ([(6,2,4)], []), - ([], []), - ([(2,2,6)], []), - ([(6,2,7)], []), - ([], []), - ([], []), - ([(2,2,10)], []), - ([(2,2,9), (6,2,11)], []), - ([(6,2,12)], []), - ([(2,2,8), (4,2,13)], []), - ([(4,2,3), (6,2,5), (8,2,14)], []), - ] - expected_data = bytes(b'\x00\x01\x00\x00\x00\x10\x00\x18\x00\n\x00\x02\x00\x1a\x00"\x00\x01latn\x00\x10\x00\x01test\x00\x1c\x00\x1a\x00\x00\x00\x01\x00\x00\x00\x01\x00\x1e\x00\x05\x00\x00\x00\x01\x00\x1c\x00\x00\x00\x01\x00\x01\x00\x00\xff\xff\x00\x01\x00\x00\x00\x01\x00\x0e\x00\x01\x00\x01\x00\x12\x00\x01\x00\x0e\x00\x01\x00\x01\x00\x02\x00\x01\x00\n\x00\x01\x00\x01\x00\x01\x00\x02\x00\x01\x00\x02\x00\x01\x00\x00') + ([], []), + ([(0, 2, 1)], []), + ([(6, 2, 2)], []), + ([], []), + ([(6, 2, 4)], []), + ([], []), + ([(2, 2, 6)], []), + ([(6, 2, 7)], []), + ([], []), + ([], []), + ([(2, 2, 10)], []), + ([(2, 2, 9), (6, 2, 11)], []), + ([(6, 2, 12)], []), + ([(2, 2, 8), (4, 2, 13)], []), + ([(4, 2, 3), (6, 2, 5), (8, 2, 14)], []), + ] + expected_data = bytes( + b'\x00\x01\x00\x00\x00\x10\x00\x18\x00\n\x00\x02\x00\x1a\x00"\x00\x01latn\x00\x10\x00\x01test\x00\x1c\x00\x1a\x00\x00\x00\x01\x00\x00\x00\x01\x00\x1e\x00\x05\x00\x00\x00\x01\x00\x1c\x00\x00\x00\x01\x00\x01\x00\x00\xff\xff\x00\x01\x00\x00\x00\x01\x00\x0e\x00\x01\x00\x01\x00\x12\x00\x01\x00\x0e\x00\x01\x00\x01\x00\x02\x00\x01\x00\n\x00\x01\x00\x01\x00\x01\x00\x02\x00\x01\x00\x02\x00\x01\x00\x00' + ) packed_data = hb.repack(table_data, obj_list) assert expected_data == packed_data + @pytest.mark.skipif(sys.platform != "darwin", reason="requires macOS") def test_sparsefont_coretext(sparsefont): buf = hb.Buffer() @@ -824,7 +947,7 @@ def test_set(): assert list(s1) == [4] s1 ^= hb.Set({5}) assert list(s1) == [4, 5] - s1 |= {8} # Update accepts set() as well + s1 |= {8} # Update accepts set() as well assert list(s1) == [4, 5, 8] assert len(s1) == 3 @@ -838,10 +961,11 @@ def test_set(): iter(iter(hb.Set({}))) + def test_map(): m1 = hb.Map() - m2 = hb.Map({1:2, 3:4}) - m3 = hb.Map({1:2, 3:4}) + m2 = hb.Map({1: 2, 3: 4}) + m3 = hb.Map({1: 2, 3: 4}) assert m1 != None assert m1 != False @@ -863,7 +987,7 @@ def test_map(): assert 1 not in m1 assert len(m1) == 1 - assert set(m2.items()) == {(1,2), (3,4)} + assert set(m2.items()) == {(1, 2), (3, 4)} assert set(m2.keys()) == {1, 3} assert set(m2) == {1, 3} assert set(m2.values()) == {2, 4} @@ -880,17 +1004,17 @@ def test_map(): iter(iter(hb.Map({}))) -def test_subset(blankfont): +def test_subset(blankfont): for planned in (False, True): - assert blankfont.get_nominal_glyph(ord('a')) == 1 - assert blankfont.get_nominal_glyph(ord('b')) == 2 - assert blankfont.get_nominal_glyph(ord('c')) == 3 - assert blankfont.get_nominal_glyph(ord('d')) == 4 - assert blankfont.get_nominal_glyph(ord('e')) == 5 + assert blankfont.get_nominal_glyph(ord("a")) == 1 + assert blankfont.get_nominal_glyph(ord("b")) == 2 + assert blankfont.get_nominal_glyph(ord("c")) == 3 + assert blankfont.get_nominal_glyph(ord("d")) == 4 + assert blankfont.get_nominal_glyph(ord("e")) == 5 inp = hb.SubsetInput() - inp.sets(hb.SubsetInputSets.UNICODE).set({ord('b')}) + inp.sets(hb.SubsetInputSets.UNICODE).set({ord("b")}) s = inp.sets(hb.SubsetInputSets.LAYOUT_FEATURE_TAG) s.clear() s.invert() @@ -907,11 +1031,11 @@ def test_subset(blankfont): assert face is not None font = hb.Font(face) - assert font.get_nominal_glyph(ord('a')) is None - assert font.get_nominal_glyph(ord('b')) == 1 - assert font.get_nominal_glyph(ord('c')) == 2 - assert font.get_nominal_glyph(ord('d')) == 3 - assert font.get_nominal_glyph(ord('e')) == 4 + assert font.get_nominal_glyph(ord("a")) is None + assert font.get_nominal_glyph(ord("b")) == 1 + assert font.get_nominal_glyph(ord("c")) == 2 + assert font.get_nominal_glyph(ord("d")) == 3 + assert font.get_nominal_glyph(ord("e")) == 4 blob = face.blob assert blob @@ -919,11 +1043,11 @@ def test_subset(blankfont): face = hb.Face(blob) font = hb.Font(face) - assert font.get_nominal_glyph(ord('a')) is None - assert font.get_nominal_glyph(ord('b')) == 1 - assert font.get_nominal_glyph(ord('c')) == 2 - assert font.get_nominal_glyph(ord('d')) == 3 - assert font.get_nominal_glyph(ord('e')) == 4 + assert font.get_nominal_glyph(ord("a")) is None + assert font.get_nominal_glyph(ord("b")) == 1 + assert font.get_nominal_glyph(ord("c")) == 2 + assert font.get_nominal_glyph(ord("d")) == 3 + assert font.get_nominal_glyph(ord("e")) == 4 if planned: mapping = plan.old_to_new_glyph_mapping @@ -935,4 +1059,4 @@ def test_subset(blankfont): assert reverse[mapping[3]] == 3 assert len(reverse) == 5 cmap = plan.unicode_to_old_glyph_mapping - assert cmap[ord('b')] == 2 + assert cmap[ord("b")] == 2 From 642a72876cf03089dd7c5404ebdf960d0a64ee9d Mon Sep 17 00:00:00 2001 From: Khaled Hosny Date: Tue, 26 Dec 2023 16:52:20 +0200 Subject: [PATCH 6/6] Test hb-ot-math API --- tests/data/STIXTwoMath-Regular.ttf | Bin 0 -> 103880 bytes tests/test_uharfbuzz.py | 259 +++++++++++++++++++++++++++++ 2 files changed, 259 insertions(+) create mode 100644 tests/data/STIXTwoMath-Regular.ttf diff --git a/tests/data/STIXTwoMath-Regular.ttf b/tests/data/STIXTwoMath-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..adf3367656d5d812a6ee909e5748e163fd6e4284 GIT binary patch literal 103880 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

ac^g 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

XOQkjr4921X;3d` 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