diff --git a/harfrust_capi/README.md b/harfrust_capi/README.md index fac510b4..68c787e9 100644 --- a/harfrust_capi/README.md +++ b/harfrust_capi/README.md @@ -1,7 +1,8 @@ # harfrust_capi A C API for [HarfRust](https://github.com/harfbuzz/harfrust), mirroring the -shaping half of HarfBuzz's API with an `hr_` prefix in place of `hb_`. +shaping, OpenType BASE baseline, and MATH query APIs of HarfBuzz with an `hr_` +prefix in place of `hb_`. Every type, function, enumerator and constant is named and numbered to match its HarfBuzz counterpart, so C code can usually be ported by renaming `hb_` to @@ -51,9 +52,8 @@ hb_shape(font, buffer, NULL, 0); Two things to know. It cannot be combined with HarfBuzz itself in one translation unit, since the macros would rewrite HarfBuzz's own declarations; -include one or the other. And it covers only the shaping API, so anything from -the list below fails to compile rather than failing at run time, which is the -point. +include one or the other. It covers the APIs listed below, so unsupported +HarfBuzz calls fail to compile. The header is generated from `hr.h`, so the two cannot drift: @@ -99,8 +99,9 @@ and can be frozen with `hr_*_make_immutable`, after which setters are ignored. Blobs, faces, fonts, font callbacks, buffers, shape plans and `hr_shape`, along with the tags, directions, scripts, languages, features and variations they need. OpenType `BASE` baseline queries are available through -`hr_ot_layout_get_baseline` and its related functions. -Set `hr_font_set_ppem` when `BASE` device adjustments should apply; it is +`hr_ot_layout_get_baseline` and its related functions. OpenType `MATH` queries +are available through `hr_ot_math_*` functions. +Set `hr_font_set_ppem` when `BASE` or `MATH` device adjustments should apply; it is independent of the point size set by `hr_font_set_ptem`. Faces can be built two ways: over a blob with `hr_face_create`, or from a diff --git a/harfrust_capi/include/hr-hb.h b/harfrust_capi/include/hr-hb.h index 3ea736c5..ebdf0912 100644 --- a/harfrust_capi/include/hr-hb.h +++ b/harfrust_capi/include/hr-hb.h @@ -1,7 +1,7 @@ /* Generated by scripts/gen-hb-compat-header.py from hr.h. Do not edit by hand. * * HarfBuzz names, mapped onto their HarfRust counterparts. Include this in - * place of to build code written against HarfBuzz's shaping API + * place of to build code written against the supported HarfBuzz APIs * against HarfRust instead: * * #include @@ -16,9 +16,9 @@ * macros below would rewrite HarfBuzz's own declarations. Include one or the * other. * - * Only the shaping API is covered, because that is all HarfRust implements. + * Shaping, BASE baseline, and MATH query APIs are covered. * Anything else HarfBuzz offers -- drawing and painting callbacks, - * subsetting, layout table introspection, hb_set and hb_map, custom Unicode + * subsetting, other layout table introspection, hb_set and hb_map, custom Unicode * callbacks -- has no mapping here and will fail to compile, which is the * point: you find out at build time rather than at run time. The version * macros (HB_VERSION_MAJOR and friends) are also absent; call hr_version() or @@ -211,6 +211,22 @@ #define hb_ot_layout_get_baseline_with_fallback hr_ot_layout_get_baseline_with_fallback #define hb_ot_layout_get_baseline_with_fallback2 hr_ot_layout_get_baseline_with_fallback2 #define hb_ot_layout_get_horizontal_baseline_tag_for_script hr_ot_layout_get_horizontal_baseline_tag_for_script +#define hb_ot_math_constant_t hr_ot_math_constant_t +#define hb_ot_math_get_constant hr_ot_math_get_constant +#define hb_ot_math_get_glyph_assembly hr_ot_math_get_glyph_assembly +#define hb_ot_math_get_glyph_italics_correction hr_ot_math_get_glyph_italics_correction +#define hb_ot_math_get_glyph_kerning hr_ot_math_get_glyph_kerning +#define hb_ot_math_get_glyph_kernings hr_ot_math_get_glyph_kernings +#define hb_ot_math_get_glyph_top_accent_attachment hr_ot_math_get_glyph_top_accent_attachment +#define hb_ot_math_get_glyph_variants hr_ot_math_get_glyph_variants +#define hb_ot_math_get_min_connector_overlap hr_ot_math_get_min_connector_overlap +#define hb_ot_math_glyph_part_flags_t hr_ot_math_glyph_part_flags_t +#define hb_ot_math_glyph_part_t hr_ot_math_glyph_part_t +#define hb_ot_math_glyph_variant_t hr_ot_math_glyph_variant_t +#define hb_ot_math_has_data hr_ot_math_has_data +#define hb_ot_math_is_glyph_extended_shape hr_ot_math_is_glyph_extended_shape +#define hb_ot_math_kern_entry_t hr_ot_math_kern_entry_t +#define hb_ot_math_kern_t hr_ot_math_kern_t #define hb_position_t hr_position_t #define hb_reference_table_func_t hr_reference_table_func_t #define hb_script_from_iso15924_tag hr_script_from_iso15924_tag @@ -311,6 +327,69 @@ #define HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_TOP_OR_RIGHT HR_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_TOP_OR_RIGHT #define HB_OT_LAYOUT_BASELINE_TAG_MATH HR_OT_LAYOUT_BASELINE_TAG_MATH #define HB_OT_LAYOUT_BASELINE_TAG_ROMAN HR_OT_LAYOUT_BASELINE_TAG_ROMAN +#define HB_OT_MATH_CONSTANT_ACCENT_BASE_HEIGHT HR_OT_MATH_CONSTANT_ACCENT_BASE_HEIGHT +#define HB_OT_MATH_CONSTANT_AXIS_HEIGHT HR_OT_MATH_CONSTANT_AXIS_HEIGHT +#define HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT HR_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT +#define HB_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT HR_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT +#define HB_OT_MATH_CONSTANT_FLATTENED_ACCENT_BASE_HEIGHT HR_OT_MATH_CONSTANT_FLATTENED_ACCENT_BASE_HEIGHT +#define HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_DISPLAY_STYLE_SHIFT_DOWN HR_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_DISPLAY_STYLE_SHIFT_DOWN +#define HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_GAP_MIN HR_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_GAP_MIN +#define HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_SHIFT_DOWN HR_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_SHIFT_DOWN +#define HB_OT_MATH_CONSTANT_FRACTION_DENOM_DISPLAY_STYLE_GAP_MIN HR_OT_MATH_CONSTANT_FRACTION_DENOM_DISPLAY_STYLE_GAP_MIN +#define HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_DISPLAY_STYLE_SHIFT_UP HR_OT_MATH_CONSTANT_FRACTION_NUMERATOR_DISPLAY_STYLE_SHIFT_UP +#define HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_GAP_MIN HR_OT_MATH_CONSTANT_FRACTION_NUMERATOR_GAP_MIN +#define HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_SHIFT_UP HR_OT_MATH_CONSTANT_FRACTION_NUMERATOR_SHIFT_UP +#define HB_OT_MATH_CONSTANT_FRACTION_NUM_DISPLAY_STYLE_GAP_MIN HR_OT_MATH_CONSTANT_FRACTION_NUM_DISPLAY_STYLE_GAP_MIN +#define HB_OT_MATH_CONSTANT_FRACTION_RULE_THICKNESS HR_OT_MATH_CONSTANT_FRACTION_RULE_THICKNESS +#define HB_OT_MATH_CONSTANT_LOWER_LIMIT_BASELINE_DROP_MIN HR_OT_MATH_CONSTANT_LOWER_LIMIT_BASELINE_DROP_MIN +#define HB_OT_MATH_CONSTANT_LOWER_LIMIT_GAP_MIN HR_OT_MATH_CONSTANT_LOWER_LIMIT_GAP_MIN +#define HB_OT_MATH_CONSTANT_MATH_LEADING HR_OT_MATH_CONSTANT_MATH_LEADING +#define HB_OT_MATH_CONSTANT_OVERBAR_EXTRA_ASCENDER HR_OT_MATH_CONSTANT_OVERBAR_EXTRA_ASCENDER +#define HB_OT_MATH_CONSTANT_OVERBAR_RULE_THICKNESS HR_OT_MATH_CONSTANT_OVERBAR_RULE_THICKNESS +#define HB_OT_MATH_CONSTANT_OVERBAR_VERTICAL_GAP HR_OT_MATH_CONSTANT_OVERBAR_VERTICAL_GAP +#define HB_OT_MATH_CONSTANT_RADICAL_DEGREE_BOTTOM_RAISE_PERCENT HR_OT_MATH_CONSTANT_RADICAL_DEGREE_BOTTOM_RAISE_PERCENT +#define HB_OT_MATH_CONSTANT_RADICAL_DISPLAY_STYLE_VERTICAL_GAP HR_OT_MATH_CONSTANT_RADICAL_DISPLAY_STYLE_VERTICAL_GAP +#define HB_OT_MATH_CONSTANT_RADICAL_EXTRA_ASCENDER HR_OT_MATH_CONSTANT_RADICAL_EXTRA_ASCENDER +#define HB_OT_MATH_CONSTANT_RADICAL_KERN_AFTER_DEGREE HR_OT_MATH_CONSTANT_RADICAL_KERN_AFTER_DEGREE +#define HB_OT_MATH_CONSTANT_RADICAL_KERN_BEFORE_DEGREE HR_OT_MATH_CONSTANT_RADICAL_KERN_BEFORE_DEGREE +#define HB_OT_MATH_CONSTANT_RADICAL_RULE_THICKNESS HR_OT_MATH_CONSTANT_RADICAL_RULE_THICKNESS +#define HB_OT_MATH_CONSTANT_RADICAL_VERTICAL_GAP HR_OT_MATH_CONSTANT_RADICAL_VERTICAL_GAP +#define HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN HR_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN +#define HB_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN HR_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN +#define HB_OT_MATH_CONSTANT_SKEWED_FRACTION_HORIZONTAL_GAP HR_OT_MATH_CONSTANT_SKEWED_FRACTION_HORIZONTAL_GAP +#define HB_OT_MATH_CONSTANT_SKEWED_FRACTION_VERTICAL_GAP HR_OT_MATH_CONSTANT_SKEWED_FRACTION_VERTICAL_GAP +#define HB_OT_MATH_CONSTANT_SPACE_AFTER_SCRIPT HR_OT_MATH_CONSTANT_SPACE_AFTER_SCRIPT +#define HB_OT_MATH_CONSTANT_STACK_BOTTOM_DISPLAY_STYLE_SHIFT_DOWN HR_OT_MATH_CONSTANT_STACK_BOTTOM_DISPLAY_STYLE_SHIFT_DOWN +#define HB_OT_MATH_CONSTANT_STACK_BOTTOM_SHIFT_DOWN HR_OT_MATH_CONSTANT_STACK_BOTTOM_SHIFT_DOWN +#define HB_OT_MATH_CONSTANT_STACK_DISPLAY_STYLE_GAP_MIN HR_OT_MATH_CONSTANT_STACK_DISPLAY_STYLE_GAP_MIN +#define HB_OT_MATH_CONSTANT_STACK_GAP_MIN HR_OT_MATH_CONSTANT_STACK_GAP_MIN +#define HB_OT_MATH_CONSTANT_STACK_TOP_DISPLAY_STYLE_SHIFT_UP HR_OT_MATH_CONSTANT_STACK_TOP_DISPLAY_STYLE_SHIFT_UP +#define HB_OT_MATH_CONSTANT_STACK_TOP_SHIFT_UP HR_OT_MATH_CONSTANT_STACK_TOP_SHIFT_UP +#define HB_OT_MATH_CONSTANT_STRETCH_STACK_BOTTOM_SHIFT_DOWN HR_OT_MATH_CONSTANT_STRETCH_STACK_BOTTOM_SHIFT_DOWN +#define HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_ABOVE_MIN HR_OT_MATH_CONSTANT_STRETCH_STACK_GAP_ABOVE_MIN +#define HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_BELOW_MIN HR_OT_MATH_CONSTANT_STRETCH_STACK_GAP_BELOW_MIN +#define HB_OT_MATH_CONSTANT_STRETCH_STACK_TOP_SHIFT_UP HR_OT_MATH_CONSTANT_STRETCH_STACK_TOP_SHIFT_UP +#define HB_OT_MATH_CONSTANT_SUBSCRIPT_BASELINE_DROP_MIN HR_OT_MATH_CONSTANT_SUBSCRIPT_BASELINE_DROP_MIN +#define HB_OT_MATH_CONSTANT_SUBSCRIPT_SHIFT_DOWN HR_OT_MATH_CONSTANT_SUBSCRIPT_SHIFT_DOWN +#define HB_OT_MATH_CONSTANT_SUBSCRIPT_TOP_MAX HR_OT_MATH_CONSTANT_SUBSCRIPT_TOP_MAX +#define HB_OT_MATH_CONSTANT_SUB_SUPERSCRIPT_GAP_MIN HR_OT_MATH_CONSTANT_SUB_SUPERSCRIPT_GAP_MIN +#define HB_OT_MATH_CONSTANT_SUPERSCRIPT_BASELINE_DROP_MAX HR_OT_MATH_CONSTANT_SUPERSCRIPT_BASELINE_DROP_MAX +#define HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MAX_WITH_SUBSCRIPT HR_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MAX_WITH_SUBSCRIPT +#define HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MIN HR_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MIN +#define HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP HR_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP +#define HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP_CRAMPED HR_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP_CRAMPED +#define HB_OT_MATH_CONSTANT_UNDERBAR_EXTRA_DESCENDER HR_OT_MATH_CONSTANT_UNDERBAR_EXTRA_DESCENDER +#define HB_OT_MATH_CONSTANT_UNDERBAR_RULE_THICKNESS HR_OT_MATH_CONSTANT_UNDERBAR_RULE_THICKNESS +#define HB_OT_MATH_CONSTANT_UNDERBAR_VERTICAL_GAP HR_OT_MATH_CONSTANT_UNDERBAR_VERTICAL_GAP +#define HB_OT_MATH_CONSTANT_UPPER_LIMIT_BASELINE_RISE_MIN HR_OT_MATH_CONSTANT_UPPER_LIMIT_BASELINE_RISE_MIN +#define HB_OT_MATH_CONSTANT_UPPER_LIMIT_GAP_MIN HR_OT_MATH_CONSTANT_UPPER_LIMIT_GAP_MIN +#define HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER HR_OT_MATH_GLYPH_PART_FLAG_EXTENDER +#define HB_OT_MATH_KERN_BOTTOM_LEFT HR_OT_MATH_KERN_BOTTOM_LEFT +#define HB_OT_MATH_KERN_BOTTOM_RIGHT HR_OT_MATH_KERN_BOTTOM_RIGHT +#define HB_OT_MATH_KERN_TOP_LEFT HR_OT_MATH_KERN_TOP_LEFT +#define HB_OT_MATH_KERN_TOP_RIGHT HR_OT_MATH_KERN_TOP_RIGHT +#define HB_OT_TAG_MATH HR_OT_TAG_MATH +#define HB_OT_TAG_MATH_SCRIPT HR_OT_TAG_MATH_SCRIPT #define HB_SCRIPT_ADLAM HR_SCRIPT_ADLAM #define HB_SCRIPT_AHOM HR_SCRIPT_AHOM #define HB_SCRIPT_ANATOLIAN_HIEROGLYPHS HR_SCRIPT_ANATOLIAN_HIEROGLYPHS diff --git a/harfrust_capi/include/hr.h b/harfrust_capi/include/hr.h index 59529ac5..89c632e5 100644 --- a/harfrust_capi/include/hr.h +++ b/harfrust_capi/include/hr.h @@ -418,6 +418,30 @@ typedef hr_bool_t (*hr_font_get_glyph_extents_func_t)(struct hr_font_t *font, */ typedef hr_tag_t hr_ot_layout_baseline_tag_t; +typedef unsigned int hr_ot_math_constant_t; + +typedef unsigned int hr_ot_math_kern_t; + +typedef struct hr_ot_math_kern_entry_t { + hr_position_t max_correction_height; + hr_position_t kern_value; +} hr_ot_math_kern_entry_t; + +typedef struct hr_ot_math_glyph_variant_t { + hr_codepoint_t glyph; + hr_position_t advance; +} hr_ot_math_glyph_variant_t; + +typedef unsigned int hr_ot_math_glyph_part_flags_t; + +typedef struct hr_ot_math_glyph_part_t { + hr_codepoint_t glyph; + hr_position_t start_connector_length; + hr_position_t end_connector_length; + hr_position_t full_advance; + hr_ot_math_glyph_part_flags_t flags; +} hr_ot_math_glyph_part_t; + /** * Copy the data. The caller keeps ownership of the original buffer. */ @@ -1572,6 +1596,132 @@ typedef hr_tag_t hr_ot_layout_baseline_tag_t; #define HR_OT_LAYOUT_BASELINE_TAG_MATH 1835103336 +#define HR_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN 0 + +#define HR_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN 1 + +#define HR_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT 2 + +#define HR_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT 3 + +#define HR_OT_MATH_CONSTANT_MATH_LEADING 4 + +#define HR_OT_MATH_CONSTANT_AXIS_HEIGHT 5 + +#define HR_OT_MATH_CONSTANT_ACCENT_BASE_HEIGHT 6 + +#define HR_OT_MATH_CONSTANT_FLATTENED_ACCENT_BASE_HEIGHT 7 + +#define HR_OT_MATH_CONSTANT_SUBSCRIPT_SHIFT_DOWN 8 + +#define HR_OT_MATH_CONSTANT_SUBSCRIPT_TOP_MAX 9 + +#define HR_OT_MATH_CONSTANT_SUBSCRIPT_BASELINE_DROP_MIN 10 + +#define HR_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP 11 + +#define HR_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP_CRAMPED 12 + +#define HR_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MIN 13 + +#define HR_OT_MATH_CONSTANT_SUPERSCRIPT_BASELINE_DROP_MAX 14 + +#define HR_OT_MATH_CONSTANT_SUB_SUPERSCRIPT_GAP_MIN 15 + +#define HR_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MAX_WITH_SUBSCRIPT 16 + +#define HR_OT_MATH_CONSTANT_SPACE_AFTER_SCRIPT 17 + +#define HR_OT_MATH_CONSTANT_UPPER_LIMIT_GAP_MIN 18 + +#define HR_OT_MATH_CONSTANT_UPPER_LIMIT_BASELINE_RISE_MIN 19 + +#define HR_OT_MATH_CONSTANT_LOWER_LIMIT_GAP_MIN 20 + +#define HR_OT_MATH_CONSTANT_LOWER_LIMIT_BASELINE_DROP_MIN 21 + +#define HR_OT_MATH_CONSTANT_STACK_TOP_SHIFT_UP 22 + +#define HR_OT_MATH_CONSTANT_STACK_TOP_DISPLAY_STYLE_SHIFT_UP 23 + +#define HR_OT_MATH_CONSTANT_STACK_BOTTOM_SHIFT_DOWN 24 + +#define HR_OT_MATH_CONSTANT_STACK_BOTTOM_DISPLAY_STYLE_SHIFT_DOWN 25 + +#define HR_OT_MATH_CONSTANT_STACK_GAP_MIN 26 + +#define HR_OT_MATH_CONSTANT_STACK_DISPLAY_STYLE_GAP_MIN 27 + +#define HR_OT_MATH_CONSTANT_STRETCH_STACK_TOP_SHIFT_UP 28 + +#define HR_OT_MATH_CONSTANT_STRETCH_STACK_BOTTOM_SHIFT_DOWN 29 + +#define HR_OT_MATH_CONSTANT_STRETCH_STACK_GAP_ABOVE_MIN 30 + +#define HR_OT_MATH_CONSTANT_STRETCH_STACK_GAP_BELOW_MIN 31 + +#define HR_OT_MATH_CONSTANT_FRACTION_NUMERATOR_SHIFT_UP 32 + +#define HR_OT_MATH_CONSTANT_FRACTION_NUMERATOR_DISPLAY_STYLE_SHIFT_UP 33 + +#define HR_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_SHIFT_DOWN 34 + +#define HR_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_DISPLAY_STYLE_SHIFT_DOWN 35 + +#define HR_OT_MATH_CONSTANT_FRACTION_NUMERATOR_GAP_MIN 36 + +#define HR_OT_MATH_CONSTANT_FRACTION_NUM_DISPLAY_STYLE_GAP_MIN 37 + +#define HR_OT_MATH_CONSTANT_FRACTION_RULE_THICKNESS 38 + +#define HR_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_GAP_MIN 39 + +#define HR_OT_MATH_CONSTANT_FRACTION_DENOM_DISPLAY_STYLE_GAP_MIN 40 + +#define HR_OT_MATH_CONSTANT_SKEWED_FRACTION_HORIZONTAL_GAP 41 + +#define HR_OT_MATH_CONSTANT_SKEWED_FRACTION_VERTICAL_GAP 42 + +#define HR_OT_MATH_CONSTANT_OVERBAR_VERTICAL_GAP 43 + +#define HR_OT_MATH_CONSTANT_OVERBAR_RULE_THICKNESS 44 + +#define HR_OT_MATH_CONSTANT_OVERBAR_EXTRA_ASCENDER 45 + +#define HR_OT_MATH_CONSTANT_UNDERBAR_VERTICAL_GAP 46 + +#define HR_OT_MATH_CONSTANT_UNDERBAR_RULE_THICKNESS 47 + +#define HR_OT_MATH_CONSTANT_UNDERBAR_EXTRA_DESCENDER 48 + +#define HR_OT_MATH_CONSTANT_RADICAL_VERTICAL_GAP 49 + +#define HR_OT_MATH_CONSTANT_RADICAL_DISPLAY_STYLE_VERTICAL_GAP 50 + +#define HR_OT_MATH_CONSTANT_RADICAL_RULE_THICKNESS 51 + +#define HR_OT_MATH_CONSTANT_RADICAL_EXTRA_ASCENDER 52 + +#define HR_OT_MATH_CONSTANT_RADICAL_KERN_BEFORE_DEGREE 53 + +#define HR_OT_MATH_CONSTANT_RADICAL_KERN_AFTER_DEGREE 54 + +#define HR_OT_MATH_CONSTANT_RADICAL_DEGREE_BOTTOM_RAISE_PERCENT 55 + +#define HR_OT_MATH_KERN_TOP_RIGHT 0 + +#define HR_OT_MATH_KERN_TOP_LEFT 1 + +#define HR_OT_MATH_KERN_BOTTOM_RIGHT 2 + +#define HR_OT_MATH_KERN_BOTTOM_LEFT 3 + +#define HR_OT_MATH_GLYPH_PART_FLAG_EXTENDER 1 + +#define HR_OT_TAG_MATH 1296127048 + +#define HR_OT_TAG_MATH_SCRIPT 1835103336 + #ifdef __cplusplus extern "C" { #endif // __cplusplus @@ -3394,6 +3544,100 @@ void hr_ot_layout_get_baseline_with_fallback2(struct hr_font_t *font, hr_language_t _language, hr_position_t *coord); +/** + * Whether a face has a MATH table with a nonzero version. + * # Safety + * `face` must be null or a live face. + */ +hr_bool_t hr_ot_math_has_data(struct hr_face_t *face); + +/** + * Returns a MATH constant, scaled on its specified axis. + * # Safety + * `font` must be null or a live font. + */ +hr_position_t hr_ot_math_get_constant(struct hr_font_t *font, hr_ot_math_constant_t constant); + +/** + * Returns the glyph's MATH italics correction, or zero. + * # Safety + * `font` must be null or a live font. + */ +hr_position_t hr_ot_math_get_glyph_italics_correction(struct hr_font_t *font, hr_codepoint_t glyph); + +/** + * Returns the top accent attachment, falling back to half the glyph advance. + * # Safety + * `font` must be null or a live font. + */ +hr_position_t hr_ot_math_get_glyph_top_accent_attachment(struct hr_font_t *font, + hr_codepoint_t glyph); + +/** + * Whether the glyph is covered by MATH's extended-shape set. + * # Safety + * `face` must be null or a live face. + */ +hr_bool_t hr_ot_math_is_glyph_extended_shape(struct hr_face_t *face, hr_codepoint_t glyph); + +/** + * Returns a MATH kern selected in the font's scaled coordinate space. + * # Safety + * `font` must be null or a live font. + */ +hr_position_t hr_ot_math_get_glyph_kerning(struct hr_font_t *font, + hr_codepoint_t glyph, + hr_ot_math_kern_t kern, + hr_position_t correction_height); + +/** + * Returns paginated raw MATH kern entries and their total count. + * # Safety + * `font` must be null or live; `entries_count` must be null or writable; + * `kern_entries` must hold its input capacity when non-null. + */ +unsigned int hr_ot_math_get_glyph_kernings(struct hr_font_t *font, + hr_codepoint_t glyph, + hr_ot_math_kern_t kern, + unsigned int start_offset, + unsigned int *entries_count, + struct hr_ot_math_kern_entry_t *kern_entries); + +/** + * Returns paginated MATH size variants and their total count. + * # Safety + * `font` must be null or live; `variants_count` must be null or writable; + * `variants` must hold its input capacity when non-null. + */ +unsigned int hr_ot_math_get_glyph_variants(struct hr_font_t *font, + hr_codepoint_t glyph, + hr_direction_t direction, + unsigned int start_offset, + unsigned int *variants_count, + struct hr_ot_math_glyph_variant_t *variants); + +/** + * Returns the minimum overlap needed between MATH assembly parts. + * # Safety + * `font` must be null or a live font. + */ +hr_position_t hr_ot_math_get_min_connector_overlap(struct hr_font_t *font, + hr_direction_t direction); + +/** + * Returns paginated MATH assembly parts and their total count. + * # Safety + * `font` must be null or live; `parts_count` and `italics_correction` must be + * null or writable; `parts` must hold the input capacity when non-null. + */ +unsigned int hr_ot_math_get_glyph_assembly(struct hr_font_t *font, + hr_codepoint_t glyph, + hr_direction_t direction, + unsigned int start_offset, + unsigned int *parts_count, + struct hr_ot_math_glyph_part_t *parts, + hr_position_t *italics_correction); + /** * Shapes a buffer with a font, applying the given features. * diff --git a/harfrust_capi/src/lib.rs b/harfrust_capi/src/lib.rs index 819a823f..ad73084c 100644 --- a/harfrust_capi/src/lib.rs +++ b/harfrust_capi/src/lib.rs @@ -10,7 +10,7 @@ as HarfBuzz itself without collisions. # Scope This covers shaping: blobs, faces, fonts, buffers and `hr_shape`, plus OpenType -`BASE` baseline queries. It has no drawing or painting callbacks, no +`BASE` baseline and `MATH` queries. It has no drawing or painting callbacks, no subsetting, no other layout table introspection and no `hb_set` / `hb_map` containers, because HarfRust does not provide them. @@ -36,6 +36,7 @@ pub mod font; pub mod font_funcs; pub mod object; pub mod ot_layout; +pub mod ot_math; mod plan; pub mod shape; pub mod shape_plan; @@ -48,6 +49,7 @@ pub use font::*; pub use font_funcs::*; pub use object::{hr_destroy_func_t, hr_user_data_key_t}; pub use ot_layout::*; +pub use ot_math::*; pub use shape::*; pub use shape_plan::*; diff --git a/harfrust_capi/src/ot_math.rs b/harfrust_capi/src/ot_math.rs new file mode 100644 index 00000000..f45b178a --- /dev/null +++ b/harfrust_capi/src/ot_math.rs @@ -0,0 +1,478 @@ +//! OpenType MATH queries with HarfBuzz-compatible C signatures. + +use core::ffi::c_uint; + +use read_fonts::tables::math::{Math, MathConstant, MathKernCorner, MathValue, StretchAxis}; +use read_fonts::types::GlyphId; +use read_fonts::TableProvider; + +use crate::common::{ + hr_bool_t, hr_codepoint_t, hr_direction_t, hr_position_t, hr_tag_t, HR_DIRECTION_BTT, + HR_DIRECTION_TTB, +}; +use crate::face::hr_face_t; +use crate::font::hr_font_t; +use crate::object; + +pub type hr_ot_math_constant_t = c_uint; +pub type hr_ot_math_kern_t = c_uint; +pub type hr_ot_math_glyph_part_flags_t = c_uint; + +pub const HR_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN: hr_ot_math_constant_t = 0; +pub const HR_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN: hr_ot_math_constant_t = 1; +pub const HR_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT: hr_ot_math_constant_t = 2; +pub const HR_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT: hr_ot_math_constant_t = 3; +pub const HR_OT_MATH_CONSTANT_MATH_LEADING: hr_ot_math_constant_t = 4; +pub const HR_OT_MATH_CONSTANT_AXIS_HEIGHT: hr_ot_math_constant_t = 5; +pub const HR_OT_MATH_CONSTANT_ACCENT_BASE_HEIGHT: hr_ot_math_constant_t = 6; +pub const HR_OT_MATH_CONSTANT_FLATTENED_ACCENT_BASE_HEIGHT: hr_ot_math_constant_t = 7; +pub const HR_OT_MATH_CONSTANT_SUBSCRIPT_SHIFT_DOWN: hr_ot_math_constant_t = 8; +pub const HR_OT_MATH_CONSTANT_SUBSCRIPT_TOP_MAX: hr_ot_math_constant_t = 9; +pub const HR_OT_MATH_CONSTANT_SUBSCRIPT_BASELINE_DROP_MIN: hr_ot_math_constant_t = 10; +pub const HR_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP: hr_ot_math_constant_t = 11; +pub const HR_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP_CRAMPED: hr_ot_math_constant_t = 12; +pub const HR_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MIN: hr_ot_math_constant_t = 13; +pub const HR_OT_MATH_CONSTANT_SUPERSCRIPT_BASELINE_DROP_MAX: hr_ot_math_constant_t = 14; +pub const HR_OT_MATH_CONSTANT_SUB_SUPERSCRIPT_GAP_MIN: hr_ot_math_constant_t = 15; +pub const HR_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MAX_WITH_SUBSCRIPT: hr_ot_math_constant_t = 16; +pub const HR_OT_MATH_CONSTANT_SPACE_AFTER_SCRIPT: hr_ot_math_constant_t = 17; +pub const HR_OT_MATH_CONSTANT_UPPER_LIMIT_GAP_MIN: hr_ot_math_constant_t = 18; +pub const HR_OT_MATH_CONSTANT_UPPER_LIMIT_BASELINE_RISE_MIN: hr_ot_math_constant_t = 19; +pub const HR_OT_MATH_CONSTANT_LOWER_LIMIT_GAP_MIN: hr_ot_math_constant_t = 20; +pub const HR_OT_MATH_CONSTANT_LOWER_LIMIT_BASELINE_DROP_MIN: hr_ot_math_constant_t = 21; +pub const HR_OT_MATH_CONSTANT_STACK_TOP_SHIFT_UP: hr_ot_math_constant_t = 22; +pub const HR_OT_MATH_CONSTANT_STACK_TOP_DISPLAY_STYLE_SHIFT_UP: hr_ot_math_constant_t = 23; +pub const HR_OT_MATH_CONSTANT_STACK_BOTTOM_SHIFT_DOWN: hr_ot_math_constant_t = 24; +pub const HR_OT_MATH_CONSTANT_STACK_BOTTOM_DISPLAY_STYLE_SHIFT_DOWN: hr_ot_math_constant_t = 25; +pub const HR_OT_MATH_CONSTANT_STACK_GAP_MIN: hr_ot_math_constant_t = 26; +pub const HR_OT_MATH_CONSTANT_STACK_DISPLAY_STYLE_GAP_MIN: hr_ot_math_constant_t = 27; +pub const HR_OT_MATH_CONSTANT_STRETCH_STACK_TOP_SHIFT_UP: hr_ot_math_constant_t = 28; +pub const HR_OT_MATH_CONSTANT_STRETCH_STACK_BOTTOM_SHIFT_DOWN: hr_ot_math_constant_t = 29; +pub const HR_OT_MATH_CONSTANT_STRETCH_STACK_GAP_ABOVE_MIN: hr_ot_math_constant_t = 30; +pub const HR_OT_MATH_CONSTANT_STRETCH_STACK_GAP_BELOW_MIN: hr_ot_math_constant_t = 31; +pub const HR_OT_MATH_CONSTANT_FRACTION_NUMERATOR_SHIFT_UP: hr_ot_math_constant_t = 32; +pub const HR_OT_MATH_CONSTANT_FRACTION_NUMERATOR_DISPLAY_STYLE_SHIFT_UP: hr_ot_math_constant_t = 33; +pub const HR_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_SHIFT_DOWN: hr_ot_math_constant_t = 34; +pub const HR_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_DISPLAY_STYLE_SHIFT_DOWN: hr_ot_math_constant_t = + 35; +pub const HR_OT_MATH_CONSTANT_FRACTION_NUMERATOR_GAP_MIN: hr_ot_math_constant_t = 36; +pub const HR_OT_MATH_CONSTANT_FRACTION_NUM_DISPLAY_STYLE_GAP_MIN: hr_ot_math_constant_t = 37; +pub const HR_OT_MATH_CONSTANT_FRACTION_RULE_THICKNESS: hr_ot_math_constant_t = 38; +pub const HR_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_GAP_MIN: hr_ot_math_constant_t = 39; +pub const HR_OT_MATH_CONSTANT_FRACTION_DENOM_DISPLAY_STYLE_GAP_MIN: hr_ot_math_constant_t = 40; +pub const HR_OT_MATH_CONSTANT_SKEWED_FRACTION_HORIZONTAL_GAP: hr_ot_math_constant_t = 41; +pub const HR_OT_MATH_CONSTANT_SKEWED_FRACTION_VERTICAL_GAP: hr_ot_math_constant_t = 42; +pub const HR_OT_MATH_CONSTANT_OVERBAR_VERTICAL_GAP: hr_ot_math_constant_t = 43; +pub const HR_OT_MATH_CONSTANT_OVERBAR_RULE_THICKNESS: hr_ot_math_constant_t = 44; +pub const HR_OT_MATH_CONSTANT_OVERBAR_EXTRA_ASCENDER: hr_ot_math_constant_t = 45; +pub const HR_OT_MATH_CONSTANT_UNDERBAR_VERTICAL_GAP: hr_ot_math_constant_t = 46; +pub const HR_OT_MATH_CONSTANT_UNDERBAR_RULE_THICKNESS: hr_ot_math_constant_t = 47; +pub const HR_OT_MATH_CONSTANT_UNDERBAR_EXTRA_DESCENDER: hr_ot_math_constant_t = 48; +pub const HR_OT_MATH_CONSTANT_RADICAL_VERTICAL_GAP: hr_ot_math_constant_t = 49; +pub const HR_OT_MATH_CONSTANT_RADICAL_DISPLAY_STYLE_VERTICAL_GAP: hr_ot_math_constant_t = 50; +pub const HR_OT_MATH_CONSTANT_RADICAL_RULE_THICKNESS: hr_ot_math_constant_t = 51; +pub const HR_OT_MATH_CONSTANT_RADICAL_EXTRA_ASCENDER: hr_ot_math_constant_t = 52; +pub const HR_OT_MATH_CONSTANT_RADICAL_KERN_BEFORE_DEGREE: hr_ot_math_constant_t = 53; +pub const HR_OT_MATH_CONSTANT_RADICAL_KERN_AFTER_DEGREE: hr_ot_math_constant_t = 54; +pub const HR_OT_MATH_CONSTANT_RADICAL_DEGREE_BOTTOM_RAISE_PERCENT: hr_ot_math_constant_t = 55; + +pub const HR_OT_MATH_KERN_TOP_RIGHT: hr_ot_math_kern_t = 0; +pub const HR_OT_MATH_KERN_TOP_LEFT: hr_ot_math_kern_t = 1; +pub const HR_OT_MATH_KERN_BOTTOM_RIGHT: hr_ot_math_kern_t = 2; +pub const HR_OT_MATH_KERN_BOTTOM_LEFT: hr_ot_math_kern_t = 3; +pub const HR_OT_MATH_GLYPH_PART_FLAG_EXTENDER: hr_ot_math_glyph_part_flags_t = 1; +pub const HR_OT_TAG_MATH: hr_tag_t = 0x4D41_5448; +pub const HR_OT_TAG_MATH_SCRIPT: hr_tag_t = 0x6D61_7468; + +#[repr(C)] +#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)] +pub struct hr_ot_math_kern_entry_t { + pub max_correction_height: hr_position_t, + pub kern_value: hr_position_t, +} + +#[repr(C)] +#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)] +pub struct hr_ot_math_glyph_variant_t { + pub glyph: hr_codepoint_t, + pub advance: hr_position_t, +} + +#[repr(C)] +#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)] +pub struct hr_ot_math_glyph_part_t { + pub glyph: hr_codepoint_t, + pub start_connector_length: hr_position_t, + pub end_connector_length: hr_position_t, + pub full_advance: hr_position_t, + pub flags: hr_ot_math_glyph_part_flags_t, +} + +fn face_math(face: &hr_face_t) -> Option> { + face.font()?.tables().math().ok() +} + +fn font_math(font: &hr_font_t) -> Option> { + font.instance()?.tables().math().ok() +} + +fn horizontal(direction: hr_direction_t) -> bool { + !matches!(direction, HR_DIRECTION_TTB | HR_DIRECTION_BTT) +} + +fn scale(font: &hr_font_t, value: i32, x_axis: bool) -> i32 { + let scale = harfrust::Scale::new(Some((font.x_scale, font.y_scale)), font.upem()); + if x_axis { + scale.scale_x(value) + } else { + scale.scale_y(value) + } +} + +fn ppem(font: &hr_font_t, x_axis: bool) -> u16 { + u16::try_from(if x_axis { font.x_ppem } else { font.y_ppem }).unwrap_or(0) +} + +fn position(font: &hr_font_t, value: MathValue, x_axis: bool) -> i32 { + let axis_scale = if x_axis { font.x_scale } else { font.y_scale }; + let device = if ppem(font, x_axis) == 0 { + 0 + } else { + (i64::from(value.delta_px) * i64::from(axis_scale) / i64::from(ppem(font, x_axis))) as i32 + }; + scale(font, value.value, x_axis).saturating_add(device) +} + +fn corner(kern: hr_ot_math_kern_t) -> Option { + Some(match kern { + HR_OT_MATH_KERN_TOP_RIGHT => MathKernCorner::TopRight, + HR_OT_MATH_KERN_TOP_LEFT => MathKernCorner::TopLeft, + HR_OT_MATH_KERN_BOTTOM_RIGHT => MathKernCorner::BottomRight, + HR_OT_MATH_KERN_BOTTOM_LEFT => MathKernCorner::BottomLeft, + _ => return None, + }) +} + +fn axis(direction: hr_direction_t) -> StretchAxis { + if horizontal(direction) { + StretchAxis::Horizontal + } else { + StretchAxis::Vertical + } +} + +/// Whether a face has a MATH table with a nonzero version. +/// # Safety +/// `face` must be null or a live face. +#[no_mangle] +pub unsafe extern "C" fn hr_ot_math_has_data(face: *mut hr_face_t) -> hr_bool_t { + let face = unsafe { object::or_empty(face.cast_const()) }; + face_math(face).is_some_and(|math| math.version().major != 0 || math.version().minor != 0) + as hr_bool_t +} + +/// Returns a MATH constant, scaled on its specified axis. +/// # Safety +/// `font` must be null or a live font. +#[no_mangle] +pub unsafe extern "C" fn hr_ot_math_get_constant( + font: *mut hr_font_t, + constant: hr_ot_math_constant_t, +) -> hr_position_t { + let font = unsafe { object::or_empty(font.cast_const()) }; + let Some(math) = font_math(font) else { + return 0; + }; + let Ok(constants) = math.math_constants() else { + return 0; + }; + let mut index = constant; + if math.has_swapped_min_heights() && (index == 2 || index == 3) { + index = 5 - index; + } + let Some(kind) = u8::try_from(index).ok().and_then(MathConstant::new) else { + return 0; + }; + if matches!(index, 0 | 1 | 55) { + return constants.constant(kind); + } + let x_axis = matches!(index, 17 | 41 | 53 | 54); + position( + font, + constants.constant_for_ppem(kind, ppem(font, x_axis)), + x_axis, + ) +} + +/// Returns the glyph's MATH italics correction, or zero. +/// # Safety +/// `font` must be null or a live font. +#[no_mangle] +pub unsafe extern "C" fn hr_ot_math_get_glyph_italics_correction( + font: *mut hr_font_t, + glyph: hr_codepoint_t, +) -> hr_position_t { + let font = unsafe { object::or_empty(font.cast_const()) }; + font_math(font) + .and_then(|math| math.math_glyph_info().ok()) + .and_then(|info| info.math_italics_correction_info()?.ok()) + .and_then(|info| info.correction_for_ppem(GlyphId::new(glyph), ppem(font, true))) + .map_or(0, |value| position(font, value, true)) +} + +/// Returns the top accent attachment, falling back to half the glyph advance. +/// # Safety +/// `font` must be null or a live font. +#[no_mangle] +pub unsafe extern "C" fn hr_ot_math_get_glyph_top_accent_attachment( + font: *mut hr_font_t, + glyph: hr_codepoint_t, +) -> hr_position_t { + let state = unsafe { object::or_empty(font.cast_const()) }; + let value = font_math(state) + .and_then(|math| math.math_glyph_info().ok()) + .and_then(|info| info.math_top_accent_attachment()?.ok()) + .and_then(|info| info.attachment_for_ppem(GlyphId::new(glyph), ppem(state, true))); + value.map_or_else( + || state.glyph_h_advance(font, glyph) / 2, + |value| position(state, value, true), + ) +} + +/// Whether the glyph is covered by MATH's extended-shape set. +/// # Safety +/// `face` must be null or a live face. +#[no_mangle] +pub unsafe extern "C" fn hr_ot_math_is_glyph_extended_shape( + face: *mut hr_face_t, + glyph: hr_codepoint_t, +) -> hr_bool_t { + let face = unsafe { object::or_empty(face.cast_const()) }; + face_math(face) + .and_then(|math| math.math_glyph_info().ok()) + .is_some_and(|info| info.is_extended_shape(GlyphId::new(glyph))) as hr_bool_t +} + +/// Returns a MATH kern selected in the font's scaled coordinate space. +/// # Safety +/// `font` must be null or a live font. +#[no_mangle] +pub unsafe extern "C" fn hr_ot_math_get_glyph_kerning( + font: *mut hr_font_t, + glyph: hr_codepoint_t, + kern: hr_ot_math_kern_t, + correction_height: hr_position_t, +) -> hr_position_t { + let state = unsafe { object::or_empty(font.cast_const()) }; + let Some(corner) = corner(kern) else { return 0 }; + let Some(table) = font_math(state) + .and_then(|math| math.math_glyph_info().ok()) + .and_then(|info| info.math_kern_info()?.ok()) + .and_then(|info| info.kern(GlyphId::new(glyph), corner)) + else { + return 0; + }; + for (height_entry, kern_entry) in table + .entries_for_ppem(ppem(state, false)) + .zip(table.entries_for_ppem(ppem(state, true))) + { + if let Some(height) = height_entry.max_height { + let bound = position(state, height, false); + if (state.y_scale >= 0 && correction_height < bound) + || (state.y_scale < 0 && correction_height > bound) + { + return position(state, kern_entry.kern, true); + } + } else { + return position(state, kern_entry.kern, true); + } + } + 0 +} + +/// Returns paginated raw MATH kern entries and their total count. +/// # Safety +/// `font` must be null or live; `entries_count` must be null or writable; +/// `kern_entries` must hold its input capacity when non-null. +#[no_mangle] +pub unsafe extern "C" fn hr_ot_math_get_glyph_kernings( + font: *mut hr_font_t, + glyph: hr_codepoint_t, + kern: hr_ot_math_kern_t, + start_offset: c_uint, + entries_count: *mut c_uint, + kern_entries: *mut hr_ot_math_kern_entry_t, +) -> c_uint { + let capacity = unsafe { entries_count.as_ref() }.copied().unwrap_or(0) as usize; + let state = unsafe { object::or_empty(font.cast_const()) }; + let table = corner(kern).and_then(|corner| { + font_math(state) + .and_then(|math| math.math_glyph_info().ok()) + .and_then(|info| info.math_kern_info()?.ok()) + .and_then(|info| info.kern(GlyphId::new(glyph), corner)) + }); + let found = table.is_some(); + let mut written = 0; + let mut total = 0; + if let Some(table) = table { + total = table.kern_values().len() as c_uint; + if !kern_entries.is_null() { + for (height_entry, kern_entry) in table + .entries_for_ppem(ppem(state, false)) + .zip(table.entries_for_ppem(ppem(state, true))) + .skip(start_offset as usize) + .take(capacity) + { + let value = hr_ot_math_kern_entry_t { + max_correction_height: height_entry + .max_height + .map_or(i32::MAX, |height| position(state, height, false)), + kern_value: position(state, kern_entry.kern, true), + }; + unsafe { kern_entries.add(written).write(value) }; + written += 1; + } + } + } + if !kern_entries.is_null() || !found { + if let Some(count) = unsafe { entries_count.as_mut() } { + *count = written as c_uint; + } + } + total +} + +/// Returns paginated MATH size variants and their total count. +/// # Safety +/// `font` must be null or live; `variants_count` must be null or writable; +/// `variants` must hold its input capacity when non-null. +#[no_mangle] +pub unsafe extern "C" fn hr_ot_math_get_glyph_variants( + font: *mut hr_font_t, + glyph: hr_codepoint_t, + direction: hr_direction_t, + start_offset: c_uint, + variants_count: *mut c_uint, + variants: *mut hr_ot_math_glyph_variant_t, +) -> c_uint { + let capacity = unsafe { variants_count.as_ref() }.copied().unwrap_or(0) as usize; + let state = unsafe { object::or_empty(font.cast_const()) }; + let construction = font_math(state) + .and_then(|math| math.math_variants().ok()) + .and_then(|table| table.glyph_construction(GlyphId::new(glyph), axis(direction))); + let found = construction.is_some(); + let mut written = 0; + let mut total = 0; + if let Some(construction) = construction { + let records = construction.math_glyph_variant_records(); + total = records.len() as c_uint; + if !variants.is_null() { + for record in records.iter().skip(start_offset as usize).take(capacity) { + unsafe { + variants.add(written).write(hr_ot_math_glyph_variant_t { + glyph: record.variant_glyph().to_u32(), + advance: scale( + state, + record.advance_measurement().to_u16() as i32, + horizontal(direction), + ), + }); + }; + written += 1; + } + } + } + if !variants.is_null() || !found { + if let Some(count) = unsafe { variants_count.as_mut() } { + *count = written as c_uint; + } + } + total +} + +/// Returns the minimum overlap needed between MATH assembly parts. +/// # Safety +/// `font` must be null or a live font. +#[no_mangle] +pub unsafe extern "C" fn hr_ot_math_get_min_connector_overlap( + font: *mut hr_font_t, + direction: hr_direction_t, +) -> hr_position_t { + let font = unsafe { object::or_empty(font.cast_const()) }; + font_math(font) + .and_then(|math| math.math_variants().ok()) + .map_or(0, |variants| { + scale( + font, + variants.min_connector_overlap().to_u16() as i32, + horizontal(direction), + ) + }) +} + +/// Returns paginated MATH assembly parts and their total count. +/// # Safety +/// `font` must be null or live; `parts_count` and `italics_correction` must be +/// null or writable; `parts` must hold the input capacity when non-null. +#[no_mangle] +pub unsafe extern "C" fn hr_ot_math_get_glyph_assembly( + font: *mut hr_font_t, + glyph: hr_codepoint_t, + direction: hr_direction_t, + start_offset: c_uint, + parts_count: *mut c_uint, + parts: *mut hr_ot_math_glyph_part_t, + italics_correction: *mut hr_position_t, +) -> c_uint { + let capacity = unsafe { parts_count.as_ref() }.copied().unwrap_or(0) as usize; + let state = unsafe { object::or_empty(font.cast_const()) }; + let assembly = font_math(state) + .and_then(|math| math.math_variants().ok()) + .and_then(|table| table.glyph_construction(GlyphId::new(glyph), axis(direction))) + .and_then(|construction| construction.glyph_assembly()?.ok()); + let found = assembly.is_some(); + let mut written = 0; + let mut total = 0; + let mut correction = 0; + if let Some(assembly) = assembly { + let x_axis = horizontal(direction); + correction = position( + state, + assembly.italics_correction_for_ppem(ppem(state, true)), + true, + ); + let records = assembly.part_records(); + total = records.len() as c_uint; + if !parts.is_null() { + for record in records.iter().skip(start_offset as usize).take(capacity) { + unsafe { + parts.add(written).write(hr_ot_math_glyph_part_t { + glyph: record.glyph_id().to_u32(), + start_connector_length: scale( + state, + record.start_connector_length().to_u16() as i32, + x_axis, + ), + end_connector_length: scale( + state, + record.end_connector_length().to_u16() as i32, + x_axis, + ), + full_advance: scale(state, record.full_advance().to_u16() as i32, x_axis), + flags: c_uint::from(record.part_flags().bits()), + }); + }; + written += 1; + } + } + } + if !parts.is_null() || !found { + if let Some(count) = unsafe { parts_count.as_mut() } { + *count = written as c_uint; + } + } + if let Some(out) = unsafe { italics_correction.as_mut() } { + *out = correction; + } + total +} diff --git a/harfrust_capi/tests/capi.rs b/harfrust_capi/tests/capi.rs index 87513717..d653332f 100644 --- a/harfrust_capi/tests/capi.rs +++ b/harfrust_capi/tests/capi.rs @@ -28,6 +28,298 @@ const PLAIN_UPEM: c_uint = 1024; /// A variable font, for exercising variation settings. const VARIABLE_FONT: &str = "Linefont.ttf"; +#[test] +fn math_queries_scale_device_values_and_page() { + unsafe { + let path = + std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fonts/MathQuery.ttf"); + with_font_file(&path, |face, font| { + assert_eq!(hr_ot_math_has_data(face), 1); + assert_eq!(hr_ot_math_is_glyph_extended_shape(face, 1), 1); + assert_eq!(hr_ot_math_is_glyph_extended_shape(face, 0), 0); + assert_eq!( + hr_ot_math_get_constant(font, HR_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN), + 80 + ); + assert_eq!( + hr_ot_math_get_constant(font, HR_OT_MATH_CONSTANT_AXIS_HEIGHT), + 642 + ); + assert_eq!(hr_ot_math_get_constant(font, 99), 0); + assert_eq!(hr_ot_math_get_glyph_italics_correction(font, 1), 120); + assert_eq!(hr_ot_math_get_glyph_top_accent_attachment(font, 1), 300); + assert_eq!( + hr_ot_math_get_glyph_top_accent_attachment(font, 0), + hr_font_get_glyph_h_advance(font, 0) / 2 + ); + assert_eq!( + hr_ot_math_get_glyph_kerning(font, 1, HR_OT_MATH_KERN_TOP_RIGHT, 399), + 25 + ); + assert_eq!( + hr_ot_math_get_glyph_kerning(font, 1, HR_OT_MATH_KERN_TOP_RIGHT, 400), + 50 + ); + assert_eq!( + hr_ot_math_get_min_connector_overlap(font, HR_DIRECTION_LTR), + 40 + ); + + let mut count = 7; + assert_eq!( + hr_ot_math_get_glyph_kernings( + font, + 1, + HR_OT_MATH_KERN_TOP_RIGHT, + 0, + &raw mut count, + ptr::null_mut() + ), + 2 + ); + assert_eq!(count, 7); + let mut entries = [hr_ot_math_kern_entry_t::default(); 2]; + count = 1; + assert_eq!( + hr_ot_math_get_glyph_kernings( + font, + 1, + HR_OT_MATH_KERN_TOP_RIGHT, + 1, + &raw mut count, + entries.as_mut_ptr() + ), + 2 + ); + assert_eq!(count, 1); + assert_eq!(entries[0].max_correction_height, i32::MAX); + assert_eq!(entries[0].kern_value, 50); + + let mut variants = [hr_ot_math_glyph_variant_t::default(); 1]; + count = 1; + assert_eq!( + hr_ot_math_get_glyph_variants( + font, + 1, + HR_DIRECTION_TTB, + 0, + &raw mut count, + variants.as_mut_ptr() + ), + 1 + ); + assert_eq!(count, 1); + assert_eq!( + variants[0], + hr_ot_math_glyph_variant_t { + glyph: 1, + advance: 700 + } + ); + count = 1; + assert_eq!( + hr_ot_math_get_glyph_variants( + font, + 1, + HR_DIRECTION_LTR, + 0, + &raw mut count, + variants.as_mut_ptr() + ), + 1 + ); + assert_eq!(variants[0].advance, 900); + count = 7; + assert_eq!( + hr_ot_math_get_glyph_variants( + font, + 1, + HR_DIRECTION_LTR, + 0, + &raw mut count, + ptr::null_mut() + ), + 1 + ); + assert_eq!(count, 7); + count = 1; + assert_eq!( + hr_ot_math_get_glyph_variants( + font, + 1, + HR_DIRECTION_LTR, + 1, + &raw mut count, + variants.as_mut_ptr() + ), + 1 + ); + assert_eq!(count, 0); + + let mut parts = [hr_ot_math_glyph_part_t::default(); 1]; + let mut correction = 0; + count = 1; + assert_eq!( + hr_ot_math_get_glyph_assembly( + font, + 1, + HR_DIRECTION_TTB, + 0, + &raw mut count, + parts.as_mut_ptr(), + &raw mut correction + ), + 1 + ); + assert_eq!(count, 1); + assert_eq!( + parts[0], + hr_ot_math_glyph_part_t { + glyph: 1, + start_connector_length: 20, + end_connector_length: 30, + full_advance: 700, + flags: HR_OT_MATH_GLYPH_PART_FLAG_EXTENDER + } + ); + assert_eq!(correction, 70); + count = 7; + assert_eq!( + hr_ot_math_get_glyph_assembly( + font, + 1, + HR_DIRECTION_TTB, + 0, + &raw mut count, + ptr::null_mut(), + &raw mut correction + ), + 1 + ); + assert_eq!(count, 7); + assert_eq!(correction, 70); + + hr_font_set_scale(font, 4096, 2048); + hr_font_set_ppem(font, 13, 12); + assert_eq!( + hr_ot_math_get_constant(font, HR_OT_MATH_CONSTANT_AXIS_HEIGHT), + 812 + ); + assert_eq!(hr_ot_math_get_glyph_italics_correction(font, 1), 240); + assert_eq!( + hr_ot_math_get_glyph_kerning(font, 1, HR_OT_MATH_KERN_TOP_RIGHT, 569), + 680 + ); + assert_eq!( + hr_ot_math_get_glyph_kerning(font, 1, HR_OT_MATH_KERN_TOP_RIGHT, 570), + 100 + ); + count = 1; + assert_eq!( + hr_ot_math_get_glyph_kernings( + font, + 1, + HR_OT_MATH_KERN_TOP_RIGHT, + 0, + &raw mut count, + entries.as_mut_ptr() + ), + 2 + ); + assert_eq!( + entries[0], + hr_ot_math_kern_entry_t { + max_correction_height: 570, + kern_value: 680 + } + ); + assert_eq!( + hr_ot_math_get_min_connector_overlap(font, HR_DIRECTION_LTR), + 80 + ); + assert_eq!( + hr_ot_math_get_min_connector_overlap(font, HR_DIRECTION_TTB), + 40 + ); + count = 1; + assert_eq!( + hr_ot_math_get_glyph_variants( + font, + 1, + HR_DIRECTION_LTR, + 0, + &raw mut count, + variants.as_mut_ptr() + ), + 1 + ); + assert_eq!(variants[0].advance, 1800); + count = 1; + assert_eq!( + hr_ot_math_get_glyph_assembly( + font, + 1, + HR_DIRECTION_LTR, + 0, + &raw mut count, + parts.as_mut_ptr(), + &raw mut correction + ), + 1 + ); + assert_eq!(parts[0].start_connector_length, 40); + assert_eq!(parts[0].end_connector_length, 60); + assert_eq!(parts[0].full_advance, 1800); + assert_eq!(correction, 140); + hr_font_set_scale(font, 4096, -2048); + assert_eq!( + hr_ot_math_get_constant(font, HR_OT_MATH_CONSTANT_AXIS_HEIGHT), + -812 + ); + assert_eq!( + hr_ot_math_get_glyph_kerning(font, 1, HR_OT_MATH_KERN_TOP_RIGHT, -569), + 680 + ); + assert_eq!( + hr_ot_math_get_glyph_kerning(font, 1, HR_OT_MATH_KERN_TOP_RIGHT, -570), + 100 + ); + assert_eq!( + hr_ot_math_get_min_connector_overlap(font, HR_DIRECTION_TTB), + -40 + ); + }); + } +} + +#[test] +fn math_queries_without_table_return_empty_values() { + unsafe { + with_font(|face, font| { + assert_eq!(hr_ot_math_has_data(face), 0); + assert_eq!( + hr_ot_math_get_constant(font, HR_OT_MATH_CONSTANT_AXIS_HEIGHT), + 0 + ); + assert_eq!(hr_ot_math_get_glyph_italics_correction(font, 1), 0); + assert_eq!(hr_ot_math_is_glyph_extended_shape(face, 1), 0); + let mut count = 2; + assert_eq!( + hr_ot_math_get_glyph_variants( + font, + 1, + HR_DIRECTION_LTR, + 0, + &raw mut count, + ptr::null_mut() + ), + 0 + ); + assert_eq!(count, 0); + }); + } +} + #[test] fn base_baselines_follow_script_and_font_scale() { unsafe { diff --git a/harfrust_capi/tests/fonts/MathQuery.ttf b/harfrust_capi/tests/fonts/MathQuery.ttf new file mode 100644 index 00000000..91f638ab Binary files /dev/null and b/harfrust_capi/tests/fonts/MathQuery.ttf differ diff --git a/harfrust_capi/tests/fonts/README.md b/harfrust_capi/tests/fonts/README.md index 658584ef..55777976 100644 --- a/harfrust_capi/tests/fonts/README.md +++ b/harfrust_capi/tests/fonts/README.md @@ -3,3 +3,8 @@ `NotoSans-OFL.txt` license. Its default-script `icfb` BASE coordinate was changed to format 3 with a Device table covering ppem 12 through 17 and deltas 1 through 6. It tests that the C font's ppem settings affect baseline queries. + +`MathQuery.ttf` is derived from +`harfrust/tests/fonts/in-house/8d9c4b193808b8bde94389ba7831c1fc6f9e794e.ttf` +by `make_math_query.py`. It adds one covered glyph for each MATH query and +Device adjustments to AxisHeight and the kern height and value. diff --git a/harfrust_capi/tests/fonts/make_math_query.py b/harfrust_capi/tests/fonts/make_math_query.py new file mode 100644 index 00000000..07727d11 --- /dev/null +++ b/harfrust_capi/tests/fonts/make_math_query.py @@ -0,0 +1,100 @@ +"""Build a small MATH query fixture from the in-house two-glyph test font.""" + +from pathlib import Path + +from fontTools.ttLib import TTFont +from fontTools.ttLib.tables import otTables + + +ROOT = Path(__file__).resolve().parents[3] +source = ROOT / "harfrust/tests/fonts/in-house/8d9c4b193808b8bde94389ba7831c1fc6f9e794e.ttf" +target = Path(__file__).with_name("MathQuery.ttf") +font = TTFont(source) +math = font["MATH"].table + +def device(ppem, delta): + result = otTables.Device() + result.StartSize = ppem + result.EndSize = ppem + result.DeltaFormat = 3 + result.DeltaValue = [delta] + return result + + +math.MathConstants.AxisHeight.DeviceTable = device(12, 1) + + +def coverage(): + result = otTables.Coverage() + result.glyphs = ["space"] + return result + + +def value(n, hinting=None): + result = otTables.MathValueRecord() + result.Value = n + result.DeviceTable = hinting + return result + + +info = math.MathGlyphInfo +italics = otTables.MathItalicsCorrectionInfo() +italics.Coverage = coverage() +italics.ItalicsCorrectionCount = 1 +italics.ItalicsCorrection = [value(120)] +info.MathItalicsCorrectionInfo = italics + +accent = otTables.MathTopAccentAttachment() +accent.TopAccentCoverage = coverage() +accent.TopAccentAttachmentCount = 1 +accent.TopAccentAttachment = [value(300)] +info.MathTopAccentAttachment = accent +info.ExtendedShapeCoverage = coverage() + +kern = otTables.MathKern() +kern.HeightCount = 1 +kern.CorrectionHeight = [value(400, device(12, 1))] +kern.KernValue = [value(25, device(13, 2)), value(50)] +record = otTables.MathKernInfoRecord() +record.TopRightMathKern = kern +record.TopLeftMathKern = None +record.BottomRightMathKern = None +record.BottomLeftMathKern = None +kern_info = otTables.MathKernInfo() +kern_info.MathKernCoverage = coverage() +kern_info.MathKernCount = 1 +kern_info.MathKernInfoRecords = [record] +info.MathKernInfo = kern_info + + +def construction(advance): + variant = otTables.MathGlyphVariantRecord() + variant.VariantGlyph = "space" + variant.AdvanceMeasurement = advance + part = otTables.GlyphPartRecord() + part.glyph = "space" + part.StartConnectorLength = 20 + part.EndConnectorLength = 30 + part.FullAdvance = advance + part.PartFlags = 1 + assembly = otTables.GlyphAssembly() + assembly.ItalicsCorrection = value(70) + assembly.PartCount = 1 + assembly.PartRecords = [part] + result = otTables.MathGlyphConstruction() + result.GlyphAssembly = assembly + result.VariantCount = 1 + result.MathGlyphVariantRecord = [variant] + return result + + +variants = math.MathVariants +variants.VertGlyphCoverage = coverage() +variants.VertGlyphCount = 1 +variants.VertGlyphConstruction = [construction(700)] +variants.HorizGlyphCoverage = coverage() +variants.HorizGlyphCount = 1 +variants.HorizGlyphConstruction = [construction(900)] + +font.save(target) +print(target) diff --git a/scripts/gen-hb-compat-header.py b/scripts/gen-hb-compat-header.py index 43605422..23067864 100644 --- a/scripts/gen-hb-compat-header.py +++ b/scripts/gen-hb-compat-header.py @@ -51,7 +51,7 @@ /* Generated by scripts/gen-hb-compat-header.py from hr.h. Do not edit by hand. * * HarfBuzz names, mapped onto their HarfRust counterparts. Include this in - * place of to build code written against HarfBuzz's shaping API + * place of to build code written against the supported HarfBuzz APIs * against HarfRust instead: * * #include @@ -66,9 +66,9 @@ * macros below would rewrite HarfBuzz's own declarations. Include one or the * other. * - * Only the shaping API is covered, because that is all HarfRust implements. + * Shaping, BASE baseline, and MATH query APIs are covered. * Anything else HarfBuzz offers -- drawing and painting callbacks, - * subsetting, layout table introspection, hb_set and hb_map, custom Unicode + * subsetting, other layout table introspection, hb_set and hb_map, custom Unicode * callbacks -- has no mapping here and will fail to compile, which is the * point: you find out at build time rather than at run time. The version * macros (HB_VERSION_MAJOR and friends) are also absent; call hr_version() or