diff --git a/docs/harfbuzz-sections.txt b/docs/harfbuzz-sections.txt index d41a8f851..2c36b412c 100644 --- a/docs/harfbuzz-sections.txt +++ b/docs/harfbuzz-sections.txt @@ -626,9 +626,13 @@ hb_ot_layout_feature_get_lookups hb_ot_layout_feature_get_name_ids hb_ot_layout_feature_with_variations_get_lookups hb_ot_layout_get_attach_points +hb_ot_layout_get_font_extents +hb_ot_layout_get_font_extents2 hb_ot_layout_get_horizontal_baseline_tag_for_script hb_ot_layout_get_baseline +hb_ot_layout_get_baseline2 hb_ot_layout_get_baseline_with_fallback +hb_ot_layout_get_baseline_with_fallback2 hb_ot_layout_get_glyph_class hb_ot_layout_get_glyphs_in_class hb_ot_layout_get_ligature_carets diff --git a/src/hb-ot-layout-base-table.hh b/src/hb-ot-layout-base-table.hh index 8179e5acd..2b7e9e4b1 100644 --- a/src/hb-ot-layout-base-table.hh +++ b/src/hb-ot-layout-base-table.hh @@ -170,8 +170,8 @@ struct FeatMinMaxRecord { TRACE_SANITIZE (this); return_trace (likely (c->check_struct (this) && - minCoord.sanitize (c, this) && - maxCoord.sanitize (c, this))); + minCoord.sanitize (c, base) && + maxCoord.sanitize (c, base))); } protected: @@ -187,7 +187,6 @@ struct FeatMinMaxRecord * of MinMax table (may be NULL) */ public: DEFINE_SIZE_STATIC (8); - }; struct MinMax @@ -274,7 +273,7 @@ struct BaseLangSysRecord { TRACE_SANITIZE (this); return_trace (likely (c->check_struct (this) && - minMax.sanitize (c, this))); + minMax.sanitize (c, base))); } protected: @@ -297,7 +296,8 @@ struct BaseScript const BaseCoord &get_base_coord (int baseline_tag_index) const { return (this+baseValues).get_base_coord (baseline_tag_index); } - bool has_data () const { return baseValues; } + bool has_values () const { return baseValues; } + bool has_min_max () const { return defaultMinMax; /* TODO What if only per-language is present? */ } bool sanitize (hb_sanitize_context_t *c) const { @@ -383,7 +383,7 @@ struct Axis const BaseCoord **coord) const { const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag); - if (!base_script.has_data ()) + if (!base_script.has_values ()) { *coord = nullptr; return false; @@ -410,7 +410,7 @@ struct Axis const BaseCoord **max_coord) const { const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag); - if (!base_script.has_data ()) + if (!base_script.has_min_max ()) { *min_coord = *max_coord = nullptr; return false; @@ -425,8 +425,8 @@ struct Axis { TRACE_SANITIZE (this); return_trace (likely (c->check_struct (this) && - (this+baseTagList).sanitize (c) && - (this+baseScriptList).sanitize (c))); + baseTagList.sanitize (c, this) && + baseScriptList.sanitize (c, this))); } protected: @@ -473,14 +473,13 @@ struct BASE return true; } - /* TODO: Expose this separately sometime? */ bool get_min_max (hb_font_t *font, hb_direction_t direction, hb_tag_t script_tag, hb_tag_t language_tag, hb_tag_t feature_tag, hb_position_t *min, - hb_position_t *max) + hb_position_t *max) const { const BaseCoord *min_coord, *max_coord; if (!get_axis (direction).get_min_max (script_tag, language_tag, feature_tag, diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc index c66ee8cfd..cbc694ef9 100644 --- a/src/hb-ot-layout.cc +++ b/src/hb-ot-layout.cc @@ -2036,6 +2036,112 @@ hb_ot_layout_substitute_lookup (OT::hb_ot_apply_context_t *c, } #ifndef HB_NO_BASE + +static void +choose_base_tags (hb_script_t script, + hb_language_t language, + hb_tag_t *script_tag, + hb_tag_t *language_tag) +{ + hb_tag_t script_tags[HB_OT_MAX_TAGS_PER_SCRIPT]; + unsigned script_count = ARRAY_LENGTH (script_tags); + + hb_tag_t language_tags[HB_OT_MAX_TAGS_PER_LANGUAGE]; + unsigned language_count = ARRAY_LENGTH (language_tags); + + hb_ot_tags_from_script_and_language (script, language, + &script_count, script_tags, + &language_count, language_tags); + + *script_tag = script_count ? script_tags[script_count - 1] : HB_OT_TAG_DEFAULT_SCRIPT; + *language_tag = language_count ? language_tags[language_count - 1] : HB_OT_TAG_DEFAULT_LANGUAGE; +} + +/** + * hb_ot_layout_get_font_extents: + * @font: a font + * @direction: text direction. + * @script_tag: script tag. + * @language_tag: language tag. + * @extents: (out) (nullable): font extents if found. + * + * Fetches script/language-specific font extents. These values are + * looked up in the `BASE` table's `MinMax` records. + * + * If no such extents are found, the default extents for the font are + * fetched. As such, the return value of this function can for the + * most part be ignored. Note that the per-script/language extents + * do not have a line-gap value, and the line-gap is set to zero in + * that case. + * + * Return value: `true` if found script/language-specific font extents. + * + * XSince: REPLACEME + **/ +hb_bool_t +hb_ot_layout_get_font_extents (hb_font_t *font, + hb_direction_t direction, + hb_tag_t script_tag, + hb_tag_t language_tag, + hb_font_extents_t *extents) +{ + hb_position_t min, max; + if (font->face->table.BASE->get_min_max (font, direction, script_tag, language_tag, HB_TAG_NONE, + &min, &max)) + { + if (extents) + { + extents->ascender = max; + extents->descender = min; + extents->line_gap = 0; + } + return true; + } + + hb_font_get_extents_for_direction (font, direction, extents); + return false; +} + +/** + * hb_ot_layout_get_font_extents2: + * @font: a font + * @direction: text direction. + * @script: script. + * @language: (nullable): language. + * @extents: (out) (nullable): font extents if found. + * + * Fetches script/language-specific font extents. These values are + * looked up in the `BASE` table's `MinMax` records. + * + * If no such extents are found, the default extents for the font are + * fetched. As such, the return value of this function can for the + * most part be ignored. Note that the per-script/language extents + * do not have a line-gap value, and the line-gap is set to zero in + * that case. + * + * This function is like hb_ot_layout_get_font_extents() but takes + * #hb_script_t and #hb_language_t instead of OpenType #hb_tag_t. + * + * Return value: `true` if found script/language-specific font extents. + * + * XSince: REPLACEME + **/ +hb_bool_t +hb_ot_layout_get_font_extents2 (hb_font_t *font, + hb_direction_t direction, + hb_script_t script, + hb_language_t language, + hb_font_extents_t *extents) +{ + hb_tag_t script_tag, language_tag; + choose_base_tags (script, language, &script_tag, &language_tag); + return hb_ot_layout_get_font_extents (font, + direction, + script_tag, + language_tag, + extents); +} + /** * hb_ot_layout_get_horizontal_baseline_tag_for_script: * @script: a script tag. @@ -2133,6 +2239,42 @@ hb_ot_layout_get_baseline (hb_font_t *font, return font->face->table.BASE->get_baseline (font, baseline_tag, direction, script_tag, language_tag, coord); } +/** + * hb_ot_layout_get_baseline2: + * @font: a font + * @baseline_tag: a baseline tag + * @direction: text direction. + * @script: script. + * @language: (nullable): language, currently unused. + * @coord: (out) (nullable): baseline value if found. + * + * Fetches a baseline value from the face. + * + * This function is like hb_ot_layout_get_baseline() but takes + * #hb_script_t and #hb_language_t instead of OpenType #hb_tag_t. + * + * Return value: `true` if found baseline value in the font. + * + * XSince: REPLACEME + **/ +hb_bool_t +hb_ot_layout_get_baseline2 (hb_font_t *font, + hb_ot_layout_baseline_tag_t baseline_tag, + hb_direction_t direction, + hb_script_t script, + hb_language_t language, + hb_position_t *coord /* OUT. May be NULL. */) +{ + hb_tag_t script_tag, language_tag; + choose_base_tags (script, language, &script_tag, &language_tag); + return hb_ot_layout_get_baseline (font, + baseline_tag, + direction, + script_tag, + language_tag, + coord); +} + /** * hb_ot_layout_get_baseline_with_fallback: * @font: a font @@ -2355,6 +2497,41 @@ hb_ot_layout_get_baseline_with_fallback (hb_font_t *font, } } +/** + * hb_ot_layout_get_baseline_with_fallback2: + * @font: a font + * @baseline_tag: a baseline tag + * @direction: text direction. + * @script: script. + * @language: (nullable): language, currently unused. + * @coord: (out): baseline value if found. + * + * Fetches a baseline value from the face, and synthesizes + * it if the font does not have it. + * + * This function is like hb_ot_layout_get_baseline_with_fallback() but takes + * #hb_script_t and #hb_language_t instead of OpenType #hb_tag_t. + * + * XSince: REPLACEME + **/ +void +hb_ot_layout_get_baseline_with_fallback2 (hb_font_t *font, + hb_ot_layout_baseline_tag_t baseline_tag, + hb_direction_t direction, + hb_script_t script, + hb_language_t language, + hb_position_t *coord /* OUT */) +{ + hb_tag_t script_tag, language_tag; + choose_base_tags (script, language, &script_tag, &language_tag); + hb_ot_layout_get_baseline_with_fallback (font, + baseline_tag, + direction, + script_tag, + language_tag, + coord); +} + #endif diff --git a/src/hb-ot-layout.h b/src/hb-ot-layout.h index 10dcc65ac..b0fae3707 100644 --- a/src/hb-ot-layout.h +++ b/src/hb-ot-layout.h @@ -447,6 +447,20 @@ hb_ot_layout_feature_get_characters (hb_face_t *face, * BASE */ +HB_EXTERN hb_bool_t +hb_ot_layout_get_font_extents (hb_font_t *font, + hb_direction_t direction, + hb_tag_t script_tag, + hb_tag_t language_tag, + hb_font_extents_t *extents); + +HB_EXTERN hb_bool_t +hb_ot_layout_get_font_extents2 (hb_font_t *font, + hb_direction_t direction, + hb_script_t script, + hb_language_t language, + hb_font_extents_t *extents); + /** * hb_ot_layout_baseline_tag_t: * @HB_OT_LAYOUT_BASELINE_TAG_ROMAN: The baseline used by alphabetic scripts such as Latin, Cyrillic and Greek. @@ -499,6 +513,14 @@ hb_ot_layout_get_baseline (hb_font_t *font, hb_tag_t language_tag, hb_position_t *coord /* OUT. May be NULL. */); +HB_EXTERN hb_bool_t +hb_ot_layout_get_baseline2 (hb_font_t *font, + hb_ot_layout_baseline_tag_t baseline_tag, + hb_direction_t direction, + hb_script_t script, + hb_language_t language, + hb_position_t *coord /* OUT. May be NULL. */); + HB_EXTERN void hb_ot_layout_get_baseline_with_fallback (hb_font_t *font, hb_ot_layout_baseline_tag_t baseline_tag, @@ -507,6 +529,14 @@ hb_ot_layout_get_baseline_with_fallback (hb_font_t *font, hb_tag_t language_tag, hb_position_t *coord /* OUT */); +HB_EXTERN void +hb_ot_layout_get_baseline_with_fallback2 (hb_font_t *font, + hb_ot_layout_baseline_tag_t baseline_tag, + hb_direction_t direction, + hb_script_t script, + hb_language_t language, + hb_position_t *coord /* OUT */); + HB_END_DECLS #endif /* HB_OT_LAYOUT_H */ diff --git a/src/hb-ot-tag.cc b/src/hb-ot-tag.cc index 547f9573d..53b6b38f6 100644 --- a/src/hb-ot-tag.cc +++ b/src/hb-ot-tag.cc @@ -412,7 +412,7 @@ parse_private_use_subtag (const char *private_use_subtag, /** * hb_ot_tags_from_script_and_language: * @script: an #hb_script_t to convert. - * @language: an #hb_language_t to convert. + * @language: (nullable): an #hb_language_t to convert. * @script_count: (inout) (optional): maximum number of script tags to retrieve (IN) * and actual number of script tags retrieved (OUT) * @script_tags: (out) (optional): array of size at least @script_count to store the diff --git a/test/api/Makefile.am b/test/api/Makefile.am index 950b738a0..efeecb4e0 100644 --- a/test/api/Makefile.am +++ b/test/api/Makefile.am @@ -33,6 +33,7 @@ noinst_PROGRAMS = $(TEST_PROGS) TEST_PROGS = \ test-aat-layout \ test-baseline \ + test-base-minmax \ test-be-glyph-advance \ test-be-num-glyphs \ test-blob \ diff --git a/test/api/fonts/base-minmax.ttf b/test/api/fonts/base-minmax.ttf new file mode 100644 index 000000000..9661b59ad Binary files /dev/null and b/test/api/fonts/base-minmax.ttf differ diff --git a/test/api/meson.build b/test/api/meson.build index ab2ab1e1d..64d7797ef 100644 --- a/test/api/meson.build +++ b/test/api/meson.build @@ -6,6 +6,7 @@ endif tests = [ 'test-aat-layout.c', 'test-baseline.c', + 'test-base-minmax.c', 'test-be-glyph-advance.c', 'test-be-num-glyphs.c', 'test-blob.c', diff --git a/test/api/test-base-minmax.c b/test/api/test-base-minmax.c new file mode 100644 index 000000000..332c2668d --- /dev/null +++ b/test/api/test-base-minmax.c @@ -0,0 +1,66 @@ +/* + * Copyright © 2023 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#include "hb-test.h" + +#include + +/* Unit tests for hb-ot-layout.h font extents */ + +static void +test_ot_layout_font_extents (void) +{ + hb_face_t *face = hb_test_open_font_file ("fonts/base-minmax.ttf"); + hb_font_t *font = hb_font_create (face); + + hb_font_extents_t extents; + + g_assert (hb_ot_layout_get_font_extents2 (font, HB_DIRECTION_LTR, + HB_SCRIPT_LATIN, HB_LANGUAGE_INVALID, + &extents)); + g_assert_cmpint (extents.ascender, ==, 2000); + + g_assert (hb_ot_layout_get_font_extents2 (font, HB_DIRECTION_LTR, + HB_SCRIPT_LATIN, hb_language_from_string ("xx", -1), + &extents)); + g_assert_cmpint (extents.ascender, ==, 2000); + + g_assert (!hb_ot_layout_get_font_extents2 (font, HB_DIRECTION_LTR, + HB_SCRIPT_ARABIC, HB_LANGUAGE_INVALID, + &extents)); + g_assert_cmpint (extents.ascender, ==, 3000); + + hb_font_destroy (font); + hb_face_destroy (face); +} + +int +main (int argc, char **argv) +{ + hb_test_init (&argc, &argv); + + hb_test_add (test_ot_layout_font_extents); + + return hb_test_run(); +} diff --git a/test/api/test-baseline.c b/test/api/test-baseline.c index 9f9542ea4..4e02cd0e2 100644 --- a/test/api/test-baseline.c +++ b/test/api/test-baseline.c @@ -41,6 +41,12 @@ test_ot_layout_base (void) &position)); g_assert_cmpint (46, ==, position); + g_assert (hb_ot_layout_get_baseline2 (font, HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_BOTTOM_OR_LEFT, HB_DIRECTION_TTB, + HB_SCRIPT_HAN, + hb_language_from_string ("en", -1), + &position)); + g_assert_cmpint (46, ==, position); + g_assert (!hb_ot_layout_get_baseline (font, HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_TOP_OR_RIGHT, HB_DIRECTION_TTB, HB_TAG ('h','a','n','i'), HB_TAG ('E','N','G',' '), @@ -63,6 +69,12 @@ test_ot_layout_base_with_fallback (void) &position); g_assert_cmpint (46, ==, position); + hb_ot_layout_get_baseline_with_fallback2 (font, HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_BOTTOM_OR_LEFT, HB_DIRECTION_TTB, + HB_SCRIPT_HAN, + hb_language_from_string ("en", -1), + &position); + g_assert_cmpint (46, ==, position); + hb_ot_layout_get_baseline_with_fallback (font, HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_TOP_OR_RIGHT, HB_DIRECTION_TTB, HB_TAG ('h','a','n','i'), HB_TAG ('E','N','G',' '),