diff options
Diffstat (limited to 'modules/text_server_adv')
-rw-r--r-- | modules/text_server_adv/SCsub | 1 | ||||
-rw-r--r-- | modules/text_server_adv/script_iterator.cpp | 20 | ||||
-rw-r--r-- | modules/text_server_adv/script_iterator.h | 2 | ||||
-rw-r--r-- | modules/text_server_adv/text_server_adv.cpp | 659 | ||||
-rw-r--r-- | modules/text_server_adv/text_server_adv.h | 1 |
5 files changed, 367 insertions, 316 deletions
diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub index 6691f86e60..7cd4db6f67 100644 --- a/modules/text_server_adv/SCsub +++ b/modules/text_server_adv/SCsub @@ -64,6 +64,7 @@ if env["builtin_harfbuzz"]: #'src/hb-gobject-structs.cc', "src/hb-icu.cc", "src/hb-map.cc", + "src/hb-ms-feature-ranges.cc", "src/hb-number.cc", "src/hb-ot-cff1-table.cc", "src/hb-ot-cff2-table.cc", diff --git a/modules/text_server_adv/script_iterator.cpp b/modules/text_server_adv/script_iterator.cpp index f9bbd25a5f..d1e849def8 100644 --- a/modules/text_server_adv/script_iterator.cpp +++ b/modules/text_server_adv/script_iterator.cpp @@ -30,6 +30,8 @@ #include "script_iterator.h" +// This implementation is derived from ICU: icu4c/source/extra/scrptrun/scrptrun.cpp + bool ScriptIterator::same_script(int32_t p_script_one, int32_t p_script_two) { return p_script_one <= USCRIPT_INHERITED || p_script_two <= USCRIPT_INHERITED || p_script_one == p_script_two; } @@ -48,7 +50,8 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length p_start = 0; } - ParenStackEntry paren_stack[128]; + int paren_size = PAREN_STACK_DEPTH; + ParenStackEntry *paren_stack = (ParenStackEntry *)memalloc(paren_size * sizeof(ParenStackEntry)); int script_start; int script_end = p_start; @@ -64,13 +67,22 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length UChar32 ch = str[script_end]; UScriptCode sc = uscript_getScript(ch, &err); if (U_FAILURE(err)) { + memfree(paren_stack); ERR_FAIL_MSG(u_errorName(err)); } if (u_getIntPropertyValue(ch, UCHAR_BIDI_PAIRED_BRACKET_TYPE) != U_BPT_NONE) { if (u_getIntPropertyValue(ch, UCHAR_BIDI_PAIRED_BRACKET_TYPE) == U_BPT_OPEN) { - paren_stack[++paren_sp].pair_index = ch; + // If it's an open character, push it onto the stack. + paren_sp++; + if (unlikely(paren_sp >= paren_size)) { + // If the stack is full, allocate more space to handle deeply nested parentheses. This is unlikely to happen with any real text. + paren_size += PAREN_STACK_DEPTH; + paren_stack = (ParenStackEntry *)memrealloc(paren_stack, paren_size * sizeof(ParenStackEntry)); + } + paren_stack[paren_sp].pair_index = ch; paren_stack[paren_sp].script_code = script_code; } else if (paren_sp >= 0) { + // If it's a close character, find the matching open on the stack, and use that script code. Any non-matching open characters above it on the stack will be poped. UChar32 paired_ch = u_getBidiPairedBracket(ch); while (paren_sp >= 0 && paren_stack[paren_sp].pair_index != paired_ch) { paren_sp -= 1; @@ -87,11 +99,13 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length if (same_script(script_code, sc)) { if (script_code <= USCRIPT_INHERITED && sc > USCRIPT_INHERITED) { script_code = sc; + // Now that we have a final script code, fix any open characters we pushed before we knew the script code. while (start_sp < paren_sp) { paren_stack[++start_sp].script_code = script_code; } } if ((u_getIntPropertyValue(ch, UCHAR_BIDI_PAIRED_BRACKET_TYPE) == U_BPT_CLOSE) && paren_sp >= 0) { + // If this character is a close paired character pop the matching open character from the stack. paren_sp -= 1; if (start_sp >= 0) { start_sp -= 1; @@ -109,4 +123,6 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length script_ranges.push_back(rng); } while (script_end < p_length); + + memfree(paren_stack); } diff --git a/modules/text_server_adv/script_iterator.h b/modules/text_server_adv/script_iterator.h index 896a0e5c15..5efd40f7c4 100644 --- a/modules/text_server_adv/script_iterator.h +++ b/modules/text_server_adv/script_iterator.h @@ -43,6 +43,8 @@ #include <hb.h> class ScriptIterator { + static const int PAREN_STACK_DEPTH = 128; + public: struct ScriptRange { int start = 0; diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index 78a87be971..ef1411fb3b 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -30,6 +30,7 @@ #include "text_server_adv.h" +#include "core/error/error_macros.h" #include "core/string/print_string.h" #include "core/string/translation.h" @@ -130,20 +131,6 @@ hb_position_t TextServerAdvanced::hb_bmp_get_glyph_h_kerning(hb_font_t *p_font, return bm_font->face->kerning_map[Vector2i(p_left_glyph, p_right_glyph)].x * 64; } -hb_position_t TextServerAdvanced::hb_bmp_get_glyph_v_kerning(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_left_glyph, hb_codepoint_t p_right_glyph, void *p_user_data) { - const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data); - - if (!bm_font->face) { - return 0; - } - - if (!bm_font->face->kerning_map.has(Vector2i(p_left_glyph, p_right_glyph))) { - return 0; - } - - return bm_font->face->kerning_map[Vector2i(p_left_glyph, p_right_glyph)].y * 64; -} - hb_bool_t TextServerAdvanced::hb_bmp_get_glyph_v_origin(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, hb_position_t *r_x, hb_position_t *r_y, void *p_user_data) { const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data); @@ -204,7 +191,6 @@ void TextServerAdvanced::hb_bmp_create_font_funcs() { hb_font_funcs_set_glyph_v_advance_func(funcs, hb_bmp_get_glyph_v_advance, nullptr, nullptr); hb_font_funcs_set_glyph_v_origin_func(funcs, hb_bmp_get_glyph_v_origin, nullptr, nullptr); hb_font_funcs_set_glyph_h_kerning_func(funcs, hb_bmp_get_glyph_h_kerning, nullptr, nullptr); - hb_font_funcs_set_glyph_v_kerning_func(funcs, hb_bmp_get_glyph_v_kerning, nullptr, nullptr); hb_font_funcs_set_glyph_extents_func(funcs, hb_bmp_get_glyph_extents, nullptr, nullptr); hb_font_funcs_make_immutable(funcs); @@ -440,266 +426,260 @@ bool TextServerAdvanced::is_locale_right_to_left(const String &p_locale) { } } -struct FeatureInfo { - int32_t tag; - String name; -}; +static Map<StringName, int32_t> feature_sets; -static FeatureInfo feature_set[] = { +static void _insert_feature_sets() { // Registered OpenType feature tags. - { HB_TAG('a', 'a', 'l', 't'), "access_all_alternates" }, - { HB_TAG('a', 'b', 'v', 'f'), "above_base_forms" }, - { HB_TAG('a', 'b', 'v', 'm'), "above_base_mark_positioning" }, - { HB_TAG('a', 'b', 'v', 's'), "above_base_substitutions" }, - { HB_TAG('a', 'f', 'r', 'c'), "alternative_fractions" }, - { HB_TAG('a', 'k', 'h', 'n'), "akhands" }, - { HB_TAG('b', 'l', 'w', 'f'), "below_base_forms" }, - { HB_TAG('b', 'l', 'w', 'm'), "below_base_mark_positioning" }, - { HB_TAG('b', 'l', 'w', 's'), "below_base_substitutions" }, - { HB_TAG('c', 'a', 'l', 't'), "contextual_alternates" }, - { HB_TAG('c', 'a', 's', 'e'), "case_sensitive_forms" }, - { HB_TAG('c', 'c', 'm', 'p'), "glyph_composition" }, - { HB_TAG('c', 'f', 'a', 'r'), "conjunct_form_after_ro" }, - { HB_TAG('c', 'j', 'c', 't'), "conjunct_forms" }, - { HB_TAG('c', 'l', 'i', 'g'), "contextual_ligatures" }, - { HB_TAG('c', 'p', 'c', 't'), "centered_cjk_punctuation" }, - { HB_TAG('c', 'p', 's', 'p'), "capital_spacing" }, - { HB_TAG('c', 's', 'w', 'h'), "contextual_swash" }, - { HB_TAG('c', 'u', 'r', 's'), "cursive_positioning" }, - { HB_TAG('c', 'v', '0', '1'), "character_variant_01" }, - { HB_TAG('c', 'v', '0', '2'), "character_variant_02" }, - { HB_TAG('c', 'v', '0', '3'), "character_variant_03" }, - { HB_TAG('c', 'v', '0', '4'), "character_variant_04" }, - { HB_TAG('c', 'v', '0', '5'), "character_variant_05" }, - { HB_TAG('c', 'v', '0', '6'), "character_variant_06" }, - { HB_TAG('c', 'v', '0', '7'), "character_variant_07" }, - { HB_TAG('c', 'v', '0', '8'), "character_variant_08" }, - { HB_TAG('c', 'v', '0', '9'), "character_variant_09" }, - { HB_TAG('c', 'v', '1', '0'), "character_variant_10" }, - { HB_TAG('c', 'v', '1', '1'), "character_variant_11" }, - { HB_TAG('c', 'v', '1', '2'), "character_variant_12" }, - { HB_TAG('c', 'v', '1', '3'), "character_variant_13" }, - { HB_TAG('c', 'v', '1', '4'), "character_variant_14" }, - { HB_TAG('c', 'v', '1', '5'), "character_variant_15" }, - { HB_TAG('c', 'v', '1', '6'), "character_variant_16" }, - { HB_TAG('c', 'v', '1', '7'), "character_variant_17" }, - { HB_TAG('c', 'v', '1', '8'), "character_variant_18" }, - { HB_TAG('c', 'v', '1', '9'), "character_variant_19" }, - { HB_TAG('c', 'v', '2', '0'), "character_variant_20" }, - { HB_TAG('c', 'v', '2', '1'), "character_variant_21" }, - { HB_TAG('c', 'v', '2', '2'), "character_variant_22" }, - { HB_TAG('c', 'v', '2', '3'), "character_variant_23" }, - { HB_TAG('c', 'v', '2', '4'), "character_variant_24" }, - { HB_TAG('c', 'v', '2', '5'), "character_variant_25" }, - { HB_TAG('c', 'v', '2', '6'), "character_variant_26" }, - { HB_TAG('c', 'v', '2', '7'), "character_variant_27" }, - { HB_TAG('c', 'v', '2', '8'), "character_variant_28" }, - { HB_TAG('c', 'v', '2', '9'), "character_variant_29" }, - { HB_TAG('c', 'v', '3', '0'), "character_variant_30" }, - { HB_TAG('c', 'v', '3', '1'), "character_variant_31" }, - { HB_TAG('c', 'v', '3', '2'), "character_variant_32" }, - { HB_TAG('c', 'v', '3', '3'), "character_variant_33" }, - { HB_TAG('c', 'v', '3', '4'), "character_variant_34" }, - { HB_TAG('c', 'v', '3', '5'), "character_variant_35" }, - { HB_TAG('c', 'v', '3', '6'), "character_variant_36" }, - { HB_TAG('c', 'v', '3', '7'), "character_variant_37" }, - { HB_TAG('c', 'v', '3', '8'), "character_variant_38" }, - { HB_TAG('c', 'v', '3', '9'), "character_variant_39" }, - { HB_TAG('c', 'v', '4', '0'), "character_variant_40" }, - { HB_TAG('c', 'v', '4', '1'), "character_variant_41" }, - { HB_TAG('c', 'v', '4', '2'), "character_variant_42" }, - { HB_TAG('c', 'v', '4', '3'), "character_variant_43" }, - { HB_TAG('c', 'v', '4', '4'), "character_variant_44" }, - { HB_TAG('c', 'v', '4', '5'), "character_variant_45" }, - { HB_TAG('c', 'v', '4', '6'), "character_variant_46" }, - { HB_TAG('c', 'v', '4', '7'), "character_variant_47" }, - { HB_TAG('c', 'v', '4', '8'), "character_variant_48" }, - { HB_TAG('c', 'v', '4', '9'), "character_variant_49" }, - { HB_TAG('c', 'v', '5', '0'), "character_variant_50" }, - { HB_TAG('c', 'v', '5', '1'), "character_variant_51" }, - { HB_TAG('c', 'v', '5', '2'), "character_variant_52" }, - { HB_TAG('c', 'v', '5', '3'), "character_variant_53" }, - { HB_TAG('c', 'v', '5', '4'), "character_variant_54" }, - { HB_TAG('c', 'v', '5', '5'), "character_variant_55" }, - { HB_TAG('c', 'v', '5', '6'), "character_variant_56" }, - { HB_TAG('c', 'v', '5', '7'), "character_variant_57" }, - { HB_TAG('c', 'v', '5', '8'), "character_variant_58" }, - { HB_TAG('c', 'v', '5', '9'), "character_variant_59" }, - { HB_TAG('c', 'v', '6', '0'), "character_variant_60" }, - { HB_TAG('c', 'v', '6', '1'), "character_variant_61" }, - { HB_TAG('c', 'v', '6', '2'), "character_variant_62" }, - { HB_TAG('c', 'v', '6', '3'), "character_variant_63" }, - { HB_TAG('c', 'v', '6', '4'), "character_variant_64" }, - { HB_TAG('c', 'v', '6', '5'), "character_variant_65" }, - { HB_TAG('c', 'v', '6', '6'), "character_variant_66" }, - { HB_TAG('c', 'v', '6', '7'), "character_variant_67" }, - { HB_TAG('c', 'v', '6', '8'), "character_variant_68" }, - { HB_TAG('c', 'v', '6', '9'), "character_variant_69" }, - { HB_TAG('c', 'v', '7', '0'), "character_variant_70" }, - { HB_TAG('c', 'v', '7', '1'), "character_variant_71" }, - { HB_TAG('c', 'v', '7', '2'), "character_variant_72" }, - { HB_TAG('c', 'v', '7', '3'), "character_variant_73" }, - { HB_TAG('c', 'v', '7', '4'), "character_variant_74" }, - { HB_TAG('c', 'v', '7', '5'), "character_variant_75" }, - { HB_TAG('c', 'v', '7', '6'), "character_variant_76" }, - { HB_TAG('c', 'v', '7', '7'), "character_variant_77" }, - { HB_TAG('c', 'v', '7', '8'), "character_variant_78" }, - { HB_TAG('c', 'v', '7', '9'), "character_variant_79" }, - { HB_TAG('c', 'v', '8', '0'), "character_variant_80" }, - { HB_TAG('c', 'v', '8', '1'), "character_variant_81" }, - { HB_TAG('c', 'v', '8', '2'), "character_variant_82" }, - { HB_TAG('c', 'v', '8', '3'), "character_variant_83" }, - { HB_TAG('c', 'v', '8', '4'), "character_variant_84" }, - { HB_TAG('c', 'v', '8', '5'), "character_variant_85" }, - { HB_TAG('c', 'v', '8', '6'), "character_variant_86" }, - { HB_TAG('c', 'v', '8', '7'), "character_variant_87" }, - { HB_TAG('c', 'v', '8', '8'), "character_variant_88" }, - { HB_TAG('c', 'v', '8', '9'), "character_variant_89" }, - { HB_TAG('c', 'v', '9', '0'), "character_variant_90" }, - { HB_TAG('c', 'v', '9', '1'), "character_variant_91" }, - { HB_TAG('c', 'v', '9', '2'), "character_variant_92" }, - { HB_TAG('c', 'v', '9', '3'), "character_variant_93" }, - { HB_TAG('c', 'v', '9', '4'), "character_variant_94" }, - { HB_TAG('c', 'v', '9', '5'), "character_variant_95" }, - { HB_TAG('c', 'v', '9', '6'), "character_variant_96" }, - { HB_TAG('c', 'v', '9', '7'), "character_variant_97" }, - { HB_TAG('c', 'v', '9', '8'), "character_variant_98" }, - { HB_TAG('c', 'v', '9', '9'), "character_variant_99" }, - { HB_TAG('c', '2', 'p', 'c'), "petite_capitals_from_capitals" }, - { HB_TAG('c', '2', 's', 'c'), "small_capitals_from_capitals" }, - { HB_TAG('d', 'i', 's', 't'), "distances" }, - { HB_TAG('d', 'l', 'i', 'g'), "discretionary_ligatures" }, - { HB_TAG('d', 'n', 'o', 'm'), "denominators" }, - { HB_TAG('d', 't', 'l', 's'), "dotless_forms" }, - { HB_TAG('e', 'x', 'p', 't'), "expert_forms" }, - { HB_TAG('f', 'a', 'l', 't'), "final_glyph_on_line_alternates" }, - { HB_TAG('f', 'i', 'n', '2'), "terminal_forms_2" }, - { HB_TAG('f', 'i', 'n', '3'), "terminal_forms_3" }, - { HB_TAG('f', 'i', 'n', 'a'), "terminal_forms" }, - { HB_TAG('f', 'l', 'a', 'c'), "flattened_accent_forms" }, - { HB_TAG('f', 'r', 'a', 'c'), "fractions" }, - { HB_TAG('f', 'w', 'i', 'd'), "full_widths" }, - { HB_TAG('h', 'a', 'l', 'f'), "half_forms" }, - { HB_TAG('h', 'a', 'l', 'n'), "halant_forms" }, - { HB_TAG('h', 'a', 'l', 't'), "alternate_half_widths" }, - { HB_TAG('h', 'i', 's', 't'), "historical_forms" }, - { HB_TAG('h', 'k', 'n', 'a'), "horizontal_kana_alternates" }, - { HB_TAG('h', 'l', 'i', 'g'), "historical_ligatures" }, - { HB_TAG('h', 'n', 'g', 'l'), "hangul" }, - { HB_TAG('h', 'o', 'j', 'o'), "hojo_kanji_forms" }, - { HB_TAG('h', 'w', 'i', 'd'), "half_widths" }, - { HB_TAG('i', 'n', 'i', 't'), "initial_forms" }, - { HB_TAG('i', 's', 'o', 'l'), "isolated_forms" }, - { HB_TAG('i', 't', 'a', 'l'), "italics" }, - { HB_TAG('j', 'a', 'l', 't'), "justification_alternates" }, - { HB_TAG('j', 'p', '7', '8'), "jis78_forms" }, - { HB_TAG('j', 'p', '8', '3'), "jis83_forms" }, - { HB_TAG('j', 'p', '9', '0'), "jis90_forms" }, - { HB_TAG('j', 'p', '0', '4'), "jis2004_forms" }, - { HB_TAG('k', 'e', 'r', 'n'), "kerning" }, - { HB_TAG('l', 'f', 'b', 'd'), "left_bounds" }, - { HB_TAG('l', 'i', 'g', 'a'), "standard_ligatures" }, - { HB_TAG('l', 'j', 'm', 'o'), "leading_jamo_forms" }, - { HB_TAG('l', 'n', 'u', 'm'), "lining_figures" }, - { HB_TAG('l', 'o', 'c', 'l'), "localized_forms" }, - { HB_TAG('l', 't', 'r', 'a'), "left_to_right_alternates" }, - { HB_TAG('l', 't', 'r', 'm'), "left_to_right_mirrored_forms" }, - { HB_TAG('m', 'a', 'r', 'k'), "mark_positioning" }, - { HB_TAG('m', 'e', 'd', '2'), "medial_forms_2" }, - { HB_TAG('m', 'e', 'd', 'i'), "medial_forms" }, - { HB_TAG('m', 'g', 'r', 'k'), "mathematical_greek" }, - { HB_TAG('m', 'k', 'm', 'k'), "mark_to_mark_positioning" }, - { HB_TAG('m', 's', 'e', 't'), "mark_positioning_via_substitution" }, - { HB_TAG('n', 'a', 'l', 't'), "alternate_annotation_forms" }, - { HB_TAG('n', 'l', 'c', 'k'), "nlc_kanji_forms" }, - { HB_TAG('n', 'u', 'k', 't'), "nukta_forms" }, - { HB_TAG('n', 'u', 'm', 'r'), "numerators" }, - { HB_TAG('o', 'n', 'u', 'm'), "oldstyle_figures" }, - { HB_TAG('o', 'p', 'b', 'd'), "optical_bounds" }, - { HB_TAG('o', 'r', 'd', 'n'), "ordinals" }, - { HB_TAG('o', 'r', 'n', 'm'), "ornaments" }, - { HB_TAG('p', 'a', 'l', 't'), "proportional_alternate_widths" }, - { HB_TAG('p', 'c', 'a', 'p'), "petite_capitals" }, - { HB_TAG('p', 'k', 'n', 'a'), "proportional_kana" }, - { HB_TAG('p', 'n', 'u', 'm'), "proportional_figures" }, - { HB_TAG('p', 'r', 'e', 'f'), "pre_base_forms" }, - { HB_TAG('p', 'r', 'e', 's'), "pre_base_substitutions" }, - { HB_TAG('p', 's', 't', 'f'), "post_base_forms" }, - { HB_TAG('p', 's', 't', 's'), "post_base_substitutions" }, - { HB_TAG('p', 'w', 'i', 'd'), "proportional_widths" }, - { HB_TAG('q', 'w', 'i', 'd'), "quarter_widths" }, - { HB_TAG('r', 'a', 'n', 'd'), "randomize" }, - { HB_TAG('r', 'c', 'l', 't'), "required_contextual_alternates" }, - { HB_TAG('r', 'k', 'r', 'f'), "rakar_forms" }, - { HB_TAG('r', 'l', 'i', 'g'), "required_ligatures" }, - { HB_TAG('r', 'p', 'h', 'f'), "reph_forms" }, - { HB_TAG('r', 't', 'b', 'd'), "right_bounds" }, - { HB_TAG('r', 't', 'l', 'a'), "right_to_left_alternates" }, - { HB_TAG('r', 't', 'l', 'm'), "right_to_left_mirrored_forms" }, - { HB_TAG('r', 'u', 'b', 'y'), "ruby_notation_forms" }, - { HB_TAG('r', 'v', 'r', 'n'), "required_variation_alternates" }, - { HB_TAG('s', 'a', 'l', 't'), "stylistic_alternates" }, - { HB_TAG('s', 'i', 'n', 'f'), "scientific_inferiors" }, - { HB_TAG('s', 'i', 'z', 'e'), "optical_size" }, - { HB_TAG('s', 'm', 'c', 'p'), "small_capitals" }, - { HB_TAG('s', 'm', 'p', 'l'), "simplified_forms" }, - { HB_TAG('s', 's', '0', '1'), "stylistic_set_01" }, - { HB_TAG('s', 's', '0', '2'), "stylistic_set_02" }, - { HB_TAG('s', 's', '0', '3'), "stylistic_set_03" }, - { HB_TAG('s', 's', '0', '4'), "stylistic_set_04" }, - { HB_TAG('s', 's', '0', '5'), "stylistic_set_05" }, - { HB_TAG('s', 's', '0', '6'), "stylistic_set_06" }, - { HB_TAG('s', 's', '0', '7'), "stylistic_set_07" }, - { HB_TAG('s', 's', '0', '8'), "stylistic_set_08" }, - { HB_TAG('s', 's', '0', '9'), "stylistic_set_09" }, - { HB_TAG('s', 's', '1', '0'), "stylistic_set_10" }, - { HB_TAG('s', 's', '1', '1'), "stylistic_set_11" }, - { HB_TAG('s', 's', '1', '2'), "stylistic_set_12" }, - { HB_TAG('s', 's', '1', '3'), "stylistic_set_13" }, - { HB_TAG('s', 's', '1', '4'), "stylistic_set_14" }, - { HB_TAG('s', 's', '1', '5'), "stylistic_set_15" }, - { HB_TAG('s', 's', '1', '6'), "stylistic_set_16" }, - { HB_TAG('s', 's', '1', '7'), "stylistic_set_17" }, - { HB_TAG('s', 's', '1', '8'), "stylistic_set_18" }, - { HB_TAG('s', 's', '1', '9'), "stylistic_set_19" }, - { HB_TAG('s', 's', '2', '0'), "stylistic_set_20" }, - { HB_TAG('s', 's', 't', 'y'), "math_script_style_alternates" }, - { HB_TAG('s', 't', 'c', 'h'), "stretching_glyph_decomposition" }, - { HB_TAG('s', 'u', 'b', 's'), "subscript" }, - { HB_TAG('s', 'u', 'p', 's'), "superscript" }, - { HB_TAG('s', 'w', 's', 'h'), "swash" }, - { HB_TAG('t', 'i', 't', 'l'), "titling" }, - { HB_TAG('t', 'j', 'm', 'o'), "trailing_jamo_forms" }, - { HB_TAG('t', 'n', 'a', 'm'), "traditional_name_forms" }, - { HB_TAG('t', 'n', 'u', 'm'), "tabular_figures" }, - { HB_TAG('t', 'r', 'a', 'd'), "traditional_forms" }, - { HB_TAG('t', 'w', 'i', 'd'), "third_widths" }, - { HB_TAG('u', 'n', 'i', 'c'), "unicase" }, - { HB_TAG('v', 'a', 'l', 't'), "alternate_vertical_metrics" }, - { HB_TAG('v', 'a', 't', 'u'), "vattu_variants" }, - { HB_TAG('v', 'e', 'r', 't'), "vertical_writing" }, - { HB_TAG('v', 'h', 'a', 'l'), "alternate_vertical_half_metrics" }, - { HB_TAG('v', 'j', 'm', 'o'), "vowel_jamo_forms" }, - { HB_TAG('v', 'k', 'n', 'a'), "vertical_kana_alternates" }, - { HB_TAG('v', 'k', 'r', 'n'), "vertical_kerning" }, - { HB_TAG('v', 'p', 'a', 'l'), "proportional_alternate_vertical_metrics" }, - { HB_TAG('v', 'r', 't', '2'), "vertical_alternates_and_rotation" }, - { HB_TAG('v', 'r', 't', 'r'), "vertical_alternates_for_rotation" }, - { HB_TAG('z', 'e', 'r', 'o'), "slashed_zero" }, - // Registered OpenType variation tags. - { HB_TAG('i', 't', 'a', 'l'), "italic" }, - { HB_TAG('o', 'p', 's', 'z'), "optical_size" }, - { HB_TAG('s', 'l', 'n', 't'), "slant" }, - { HB_TAG('w', 'd', 't', 'h'), "width" }, - { HB_TAG('w', 'g', 'h', 't'), "weight" }, - { 0, String() }, -}; + feature_sets.insert("access_all_alternates", HB_TAG('a', 'a', 'l', 't')); + feature_sets.insert("above_base_forms", HB_TAG('a', 'b', 'v', 'f')); + feature_sets.insert("above_base_mark_positioning", HB_TAG('a', 'b', 'v', 'm')); + feature_sets.insert("above_base_substitutions", HB_TAG('a', 'b', 'v', 's')); + feature_sets.insert("alternative_fractions", HB_TAG('a', 'f', 'r', 'c')); + feature_sets.insert("akhands", HB_TAG('a', 'k', 'h', 'n')); + feature_sets.insert("below_base_forms", HB_TAG('b', 'l', 'w', 'f')); + feature_sets.insert("below_base_mark_positioning", HB_TAG('b', 'l', 'w', 'm')); + feature_sets.insert("below_base_substitutions", HB_TAG('b', 'l', 'w', 's')); + feature_sets.insert("contextual_alternates", HB_TAG('c', 'a', 'l', 't')); + feature_sets.insert("case_sensitive_forms", HB_TAG('c', 'a', 's', 'e')); + feature_sets.insert("glyph_composition", HB_TAG('c', 'c', 'm', 'p')); + feature_sets.insert("conjunct_form_after_ro", HB_TAG('c', 'f', 'a', 'r')); + feature_sets.insert("conjunct_forms", HB_TAG('c', 'j', 'c', 't')); + feature_sets.insert("contextual_ligatures", HB_TAG('c', 'l', 'i', 'g')); + feature_sets.insert("centered_cjk_punctuation", HB_TAG('c', 'p', 'c', 't')); + feature_sets.insert("capital_spacing", HB_TAG('c', 'p', 's', 'p')); + feature_sets.insert("contextual_swash", HB_TAG('c', 's', 'w', 'h')); + feature_sets.insert("cursive_positioning", HB_TAG('c', 'u', 'r', 's')); + feature_sets.insert("character_variant_01", HB_TAG('c', 'v', '0', '1')); + feature_sets.insert("character_variant_02", HB_TAG('c', 'v', '0', '2')); + feature_sets.insert("character_variant_03", HB_TAG('c', 'v', '0', '3')); + feature_sets.insert("character_variant_04", HB_TAG('c', 'v', '0', '4')); + feature_sets.insert("character_variant_05", HB_TAG('c', 'v', '0', '5')); + feature_sets.insert("character_variant_06", HB_TAG('c', 'v', '0', '6')); + feature_sets.insert("character_variant_07", HB_TAG('c', 'v', '0', '7')); + feature_sets.insert("character_variant_08", HB_TAG('c', 'v', '0', '8')); + feature_sets.insert("character_variant_09", HB_TAG('c', 'v', '0', '9')); + feature_sets.insert("character_variant_10", HB_TAG('c', 'v', '1', '0')); + feature_sets.insert("character_variant_11", HB_TAG('c', 'v', '1', '1')); + feature_sets.insert("character_variant_12", HB_TAG('c', 'v', '1', '2')); + feature_sets.insert("character_variant_13", HB_TAG('c', 'v', '1', '3')); + feature_sets.insert("character_variant_14", HB_TAG('c', 'v', '1', '4')); + feature_sets.insert("character_variant_15", HB_TAG('c', 'v', '1', '5')); + feature_sets.insert("character_variant_16", HB_TAG('c', 'v', '1', '6')); + feature_sets.insert("character_variant_17", HB_TAG('c', 'v', '1', '7')); + feature_sets.insert("character_variant_18", HB_TAG('c', 'v', '1', '8')); + feature_sets.insert("character_variant_19", HB_TAG('c', 'v', '1', '9')); + feature_sets.insert("character_variant_20", HB_TAG('c', 'v', '2', '0')); + feature_sets.insert("character_variant_21", HB_TAG('c', 'v', '2', '1')); + feature_sets.insert("character_variant_22", HB_TAG('c', 'v', '2', '2')); + feature_sets.insert("character_variant_23", HB_TAG('c', 'v', '2', '3')); + feature_sets.insert("character_variant_24", HB_TAG('c', 'v', '2', '4')); + feature_sets.insert("character_variant_25", HB_TAG('c', 'v', '2', '5')); + feature_sets.insert("character_variant_26", HB_TAG('c', 'v', '2', '6')); + feature_sets.insert("character_variant_27", HB_TAG('c', 'v', '2', '7')); + feature_sets.insert("character_variant_28", HB_TAG('c', 'v', '2', '8')); + feature_sets.insert("character_variant_29", HB_TAG('c', 'v', '2', '9')); + feature_sets.insert("character_variant_30", HB_TAG('c', 'v', '3', '0')); + feature_sets.insert("character_variant_31", HB_TAG('c', 'v', '3', '1')); + feature_sets.insert("character_variant_32", HB_TAG('c', 'v', '3', '2')); + feature_sets.insert("character_variant_33", HB_TAG('c', 'v', '3', '3')); + feature_sets.insert("character_variant_34", HB_TAG('c', 'v', '3', '4')); + feature_sets.insert("character_variant_35", HB_TAG('c', 'v', '3', '5')); + feature_sets.insert("character_variant_36", HB_TAG('c', 'v', '3', '6')); + feature_sets.insert("character_variant_37", HB_TAG('c', 'v', '3', '7')); + feature_sets.insert("character_variant_38", HB_TAG('c', 'v', '3', '8')); + feature_sets.insert("character_variant_39", HB_TAG('c', 'v', '3', '9')); + feature_sets.insert("character_variant_40", HB_TAG('c', 'v', '4', '0')); + feature_sets.insert("character_variant_41", HB_TAG('c', 'v', '4', '1')); + feature_sets.insert("character_variant_42", HB_TAG('c', 'v', '4', '2')); + feature_sets.insert("character_variant_43", HB_TAG('c', 'v', '4', '3')); + feature_sets.insert("character_variant_44", HB_TAG('c', 'v', '4', '4')); + feature_sets.insert("character_variant_45", HB_TAG('c', 'v', '4', '5')); + feature_sets.insert("character_variant_46", HB_TAG('c', 'v', '4', '6')); + feature_sets.insert("character_variant_47", HB_TAG('c', 'v', '4', '7')); + feature_sets.insert("character_variant_48", HB_TAG('c', 'v', '4', '8')); + feature_sets.insert("character_variant_49", HB_TAG('c', 'v', '4', '9')); + feature_sets.insert("character_variant_50", HB_TAG('c', 'v', '5', '0')); + feature_sets.insert("character_variant_51", HB_TAG('c', 'v', '5', '1')); + feature_sets.insert("character_variant_52", HB_TAG('c', 'v', '5', '2')); + feature_sets.insert("character_variant_53", HB_TAG('c', 'v', '5', '3')); + feature_sets.insert("character_variant_54", HB_TAG('c', 'v', '5', '4')); + feature_sets.insert("character_variant_55", HB_TAG('c', 'v', '5', '5')); + feature_sets.insert("character_variant_56", HB_TAG('c', 'v', '5', '6')); + feature_sets.insert("character_variant_57", HB_TAG('c', 'v', '5', '7')); + feature_sets.insert("character_variant_58", HB_TAG('c', 'v', '5', '8')); + feature_sets.insert("character_variant_59", HB_TAG('c', 'v', '5', '9')); + feature_sets.insert("character_variant_60", HB_TAG('c', 'v', '6', '0')); + feature_sets.insert("character_variant_61", HB_TAG('c', 'v', '6', '1')); + feature_sets.insert("character_variant_62", HB_TAG('c', 'v', '6', '2')); + feature_sets.insert("character_variant_63", HB_TAG('c', 'v', '6', '3')); + feature_sets.insert("character_variant_64", HB_TAG('c', 'v', '6', '4')); + feature_sets.insert("character_variant_65", HB_TAG('c', 'v', '6', '5')); + feature_sets.insert("character_variant_66", HB_TAG('c', 'v', '6', '6')); + feature_sets.insert("character_variant_67", HB_TAG('c', 'v', '6', '7')); + feature_sets.insert("character_variant_68", HB_TAG('c', 'v', '6', '8')); + feature_sets.insert("character_variant_69", HB_TAG('c', 'v', '6', '9')); + feature_sets.insert("character_variant_70", HB_TAG('c', 'v', '7', '0')); + feature_sets.insert("character_variant_71", HB_TAG('c', 'v', '7', '1')); + feature_sets.insert("character_variant_72", HB_TAG('c', 'v', '7', '2')); + feature_sets.insert("character_variant_73", HB_TAG('c', 'v', '7', '3')); + feature_sets.insert("character_variant_74", HB_TAG('c', 'v', '7', '4')); + feature_sets.insert("character_variant_75", HB_TAG('c', 'v', '7', '5')); + feature_sets.insert("character_variant_76", HB_TAG('c', 'v', '7', '6')); + feature_sets.insert("character_variant_77", HB_TAG('c', 'v', '7', '7')); + feature_sets.insert("character_variant_78", HB_TAG('c', 'v', '7', '8')); + feature_sets.insert("character_variant_79", HB_TAG('c', 'v', '7', '9')); + feature_sets.insert("character_variant_80", HB_TAG('c', 'v', '8', '0')); + feature_sets.insert("character_variant_81", HB_TAG('c', 'v', '8', '1')); + feature_sets.insert("character_variant_82", HB_TAG('c', 'v', '8', '2')); + feature_sets.insert("character_variant_83", HB_TAG('c', 'v', '8', '3')); + feature_sets.insert("character_variant_84", HB_TAG('c', 'v', '8', '4')); + feature_sets.insert("character_variant_85", HB_TAG('c', 'v', '8', '5')); + feature_sets.insert("character_variant_86", HB_TAG('c', 'v', '8', '6')); + feature_sets.insert("character_variant_87", HB_TAG('c', 'v', '8', '7')); + feature_sets.insert("character_variant_88", HB_TAG('c', 'v', '8', '8')); + feature_sets.insert("character_variant_89", HB_TAG('c', 'v', '8', '9')); + feature_sets.insert("character_variant_90", HB_TAG('c', 'v', '9', '0')); + feature_sets.insert("character_variant_91", HB_TAG('c', 'v', '9', '1')); + feature_sets.insert("character_variant_92", HB_TAG('c', 'v', '9', '2')); + feature_sets.insert("character_variant_93", HB_TAG('c', 'v', '9', '3')); + feature_sets.insert("character_variant_94", HB_TAG('c', 'v', '9', '4')); + feature_sets.insert("character_variant_95", HB_TAG('c', 'v', '9', '5')); + feature_sets.insert("character_variant_96", HB_TAG('c', 'v', '9', '6')); + feature_sets.insert("character_variant_97", HB_TAG('c', 'v', '9', '7')); + feature_sets.insert("character_variant_98", HB_TAG('c', 'v', '9', '8')); + feature_sets.insert("character_variant_99", HB_TAG('c', 'v', '9', '9')); + feature_sets.insert("petite_capitals_from_capitals", HB_TAG('c', '2', 'p', 'c')); + feature_sets.insert("small_capitals_from_capitals", HB_TAG('c', '2', 's', 'c')); + feature_sets.insert("distances", HB_TAG('d', 'i', 's', 't')); + feature_sets.insert("discretionary_ligatures", HB_TAG('d', 'l', 'i', 'g')); + feature_sets.insert("denominators", HB_TAG('d', 'n', 'o', 'm')); + feature_sets.insert("dotless_forms", HB_TAG('d', 't', 'l', 's')); + feature_sets.insert("expert_forms", HB_TAG('e', 'x', 'p', 't')); + feature_sets.insert("final_glyph_on_line_alternates", HB_TAG('f', 'a', 'l', 't')); + feature_sets.insert("terminal_forms_2", HB_TAG('f', 'i', 'n', '2')); + feature_sets.insert("terminal_forms_3", HB_TAG('f', 'i', 'n', '3')); + feature_sets.insert("terminal_forms", HB_TAG('f', 'i', 'n', 'a')); + feature_sets.insert("flattened_accent_forms", HB_TAG('f', 'l', 'a', 'c')); + feature_sets.insert("fractions", HB_TAG('f', 'r', 'a', 'c')); + feature_sets.insert("full_widths", HB_TAG('f', 'w', 'i', 'd')); + feature_sets.insert("half_forms", HB_TAG('h', 'a', 'l', 'f')); + feature_sets.insert("halant_forms", HB_TAG('h', 'a', 'l', 'n')); + feature_sets.insert("alternate_half_widths", HB_TAG('h', 'a', 'l', 't')); + feature_sets.insert("historical_forms", HB_TAG('h', 'i', 's', 't')); + feature_sets.insert("horizontal_kana_alternates", HB_TAG('h', 'k', 'n', 'a')); + feature_sets.insert("historical_ligatures", HB_TAG('h', 'l', 'i', 'g')); + feature_sets.insert("hangul", HB_TAG('h', 'n', 'g', 'l')); + feature_sets.insert("hojo_kanji_forms", HB_TAG('h', 'o', 'j', 'o')); + feature_sets.insert("half_widths", HB_TAG('h', 'w', 'i', 'd')); + feature_sets.insert("initial_forms", HB_TAG('i', 'n', 'i', 't')); + feature_sets.insert("isolated_forms", HB_TAG('i', 's', 'o', 'l')); + feature_sets.insert("italics", HB_TAG('i', 't', 'a', 'l')); + feature_sets.insert("justification_alternates", HB_TAG('j', 'a', 'l', 't')); + feature_sets.insert("jis78_forms", HB_TAG('j', 'p', '7', '8')); + feature_sets.insert("jis83_forms", HB_TAG('j', 'p', '8', '3')); + feature_sets.insert("jis90_forms", HB_TAG('j', 'p', '9', '0')); + feature_sets.insert("jis2004_forms", HB_TAG('j', 'p', '0', '4')); + feature_sets.insert("kerning", HB_TAG('k', 'e', 'r', 'n')); + feature_sets.insert("left_bounds", HB_TAG('l', 'f', 'b', 'd')); + feature_sets.insert("standard_ligatures", HB_TAG('l', 'i', 'g', 'a')); + feature_sets.insert("leading_jamo_forms", HB_TAG('l', 'j', 'm', 'o')); + feature_sets.insert("lining_figures", HB_TAG('l', 'n', 'u', 'm')); + feature_sets.insert("localized_forms", HB_TAG('l', 'o', 'c', 'l')); + feature_sets.insert("left_to_right_alternates", HB_TAG('l', 't', 'r', 'a')); + feature_sets.insert("left_to_right_mirrored_forms", HB_TAG('l', 't', 'r', 'm')); + feature_sets.insert("mark_positioning", HB_TAG('m', 'a', 'r', 'k')); + feature_sets.insert("medial_forms_2", HB_TAG('m', 'e', 'd', '2')); + feature_sets.insert("medial_forms", HB_TAG('m', 'e', 'd', 'i')); + feature_sets.insert("mathematical_greek", HB_TAG('m', 'g', 'r', 'k')); + feature_sets.insert("mark_to_mark_positioning", HB_TAG('m', 'k', 'm', 'k')); + feature_sets.insert("mark_positioning_via_substitution", HB_TAG('m', 's', 'e', 't')); + feature_sets.insert("alternate_annotation_forms", HB_TAG('n', 'a', 'l', 't')); + feature_sets.insert("nlc_kanji_forms", HB_TAG('n', 'l', 'c', 'k')); + feature_sets.insert("nukta_forms", HB_TAG('n', 'u', 'k', 't')); + feature_sets.insert("numerators", HB_TAG('n', 'u', 'm', 'r')); + feature_sets.insert("oldstyle_figures", HB_TAG('o', 'n', 'u', 'm')); + feature_sets.insert("optical_bounds", HB_TAG('o', 'p', 'b', 'd')); + feature_sets.insert("ordinals", HB_TAG('o', 'r', 'd', 'n')); + feature_sets.insert("ornaments", HB_TAG('o', 'r', 'n', 'm')); + feature_sets.insert("proportional_alternate_widths", HB_TAG('p', 'a', 'l', 't')); + feature_sets.insert("petite_capitals", HB_TAG('p', 'c', 'a', 'p')); + feature_sets.insert("proportional_kana", HB_TAG('p', 'k', 'n', 'a')); + feature_sets.insert("proportional_figures", HB_TAG('p', 'n', 'u', 'm')); + feature_sets.insert("pre_base_forms", HB_TAG('p', 'r', 'e', 'f')); + feature_sets.insert("pre_base_substitutions", HB_TAG('p', 'r', 'e', 's')); + feature_sets.insert("post_base_forms", HB_TAG('p', 's', 't', 'f')); + feature_sets.insert("post_base_substitutions", HB_TAG('p', 's', 't', 's')); + feature_sets.insert("proportional_widths", HB_TAG('p', 'w', 'i', 'd')); + feature_sets.insert("quarter_widths", HB_TAG('q', 'w', 'i', 'd')); + feature_sets.insert("randomize", HB_TAG('r', 'a', 'n', 'd')); + feature_sets.insert("required_contextual_alternates", HB_TAG('r', 'c', 'l', 't')); + feature_sets.insert("rakar_forms", HB_TAG('r', 'k', 'r', 'f')); + feature_sets.insert("required_ligatures", HB_TAG('r', 'l', 'i', 'g')); + feature_sets.insert("reph_forms", HB_TAG('r', 'p', 'h', 'f')); + feature_sets.insert("right_bounds", HB_TAG('r', 't', 'b', 'd')); + feature_sets.insert("right_to_left_alternates", HB_TAG('r', 't', 'l', 'a')); + feature_sets.insert("right_to_left_mirrored_forms", HB_TAG('r', 't', 'l', 'm')); + feature_sets.insert("ruby_notation_forms", HB_TAG('r', 'u', 'b', 'y')); + feature_sets.insert("required_variation_alternates", HB_TAG('r', 'v', 'r', 'n')); + feature_sets.insert("stylistic_alternates", HB_TAG('s', 'a', 'l', 't')); + feature_sets.insert("scientific_inferiors", HB_TAG('s', 'i', 'n', 'f')); + feature_sets.insert("optical_size", HB_TAG('s', 'i', 'z', 'e')); + feature_sets.insert("small_capitals", HB_TAG('s', 'm', 'c', 'p')); + feature_sets.insert("simplified_forms", HB_TAG('s', 'm', 'p', 'l')); + feature_sets.insert("stylistic_set_01", HB_TAG('s', 's', '0', '1')); + feature_sets.insert("stylistic_set_02", HB_TAG('s', 's', '0', '2')); + feature_sets.insert("stylistic_set_03", HB_TAG('s', 's', '0', '3')); + feature_sets.insert("stylistic_set_04", HB_TAG('s', 's', '0', '4')); + feature_sets.insert("stylistic_set_05", HB_TAG('s', 's', '0', '5')); + feature_sets.insert("stylistic_set_06", HB_TAG('s', 's', '0', '6')); + feature_sets.insert("stylistic_set_07", HB_TAG('s', 's', '0', '7')); + feature_sets.insert("stylistic_set_08", HB_TAG('s', 's', '0', '8')); + feature_sets.insert("stylistic_set_09", HB_TAG('s', 's', '0', '9')); + feature_sets.insert("stylistic_set_10", HB_TAG('s', 's', '1', '0')); + feature_sets.insert("stylistic_set_11", HB_TAG('s', 's', '1', '1')); + feature_sets.insert("stylistic_set_12", HB_TAG('s', 's', '1', '2')); + feature_sets.insert("stylistic_set_13", HB_TAG('s', 's', '1', '3')); + feature_sets.insert("stylistic_set_14", HB_TAG('s', 's', '1', '4')); + feature_sets.insert("stylistic_set_15", HB_TAG('s', 's', '1', '5')); + feature_sets.insert("stylistic_set_16", HB_TAG('s', 's', '1', '6')); + feature_sets.insert("stylistic_set_17", HB_TAG('s', 's', '1', '7')); + feature_sets.insert("stylistic_set_18", HB_TAG('s', 's', '1', '8')); + feature_sets.insert("stylistic_set_19", HB_TAG('s', 's', '1', '9')); + feature_sets.insert("stylistic_set_20", HB_TAG('s', 's', '2', '0')); + feature_sets.insert("math_script_style_alternates", HB_TAG('s', 's', 't', 'y')); + feature_sets.insert("stretching_glyph_decomposition", HB_TAG('s', 't', 'c', 'h')); + feature_sets.insert("subscript", HB_TAG('s', 'u', 'b', 's')); + feature_sets.insert("superscript", HB_TAG('s', 'u', 'p', 's')); + feature_sets.insert("swash", HB_TAG('s', 'w', 's', 'h')); + feature_sets.insert("titling", HB_TAG('t', 'i', 't', 'l')); + feature_sets.insert("trailing_jamo_forms", HB_TAG('t', 'j', 'm', 'o')); + feature_sets.insert("traditional_name_forms", HB_TAG('t', 'n', 'a', 'm')); + feature_sets.insert("tabular_figures", HB_TAG('t', 'n', 'u', 'm')); + feature_sets.insert("traditional_forms", HB_TAG('t', 'r', 'a', 'd')); + feature_sets.insert("third_widths", HB_TAG('t', 'w', 'i', 'd')); + feature_sets.insert("unicase", HB_TAG('u', 'n', 'i', 'c')); + feature_sets.insert("alternate_vertical_metrics", HB_TAG('v', 'a', 'l', 't')); + feature_sets.insert("vattu_variants", HB_TAG('v', 'a', 't', 'u')); + feature_sets.insert("vertical_writing", HB_TAG('v', 'e', 'r', 't')); + feature_sets.insert("alternate_vertical_half_metrics", HB_TAG('v', 'h', 'a', 'l')); + feature_sets.insert("vowel_jamo_forms", HB_TAG('v', 'j', 'm', 'o')); + feature_sets.insert("vertical_kana_alternates", HB_TAG('v', 'k', 'n', 'a')); + feature_sets.insert("vertical_kerning", HB_TAG('v', 'k', 'r', 'n')); + feature_sets.insert("proportional_alternate_vertical_metrics", HB_TAG('v', 'p', 'a', 'l')); + feature_sets.insert("vertical_alternates_and_rotation", HB_TAG('v', 'r', 't', '2')); + feature_sets.insert("vertical_alternates_for_rotation", HB_TAG('v', 'r', 't', 'r')); + feature_sets.insert("slashed_zero", HB_TAG('z', 'e', 'r', 'o')); + // Registered OpenType variation tag. + feature_sets.insert("italic", HB_TAG('i', 't', 'a', 'l')); + feature_sets.insert("optical_size", HB_TAG('o', 'p', 's', 'z')); + feature_sets.insert("slant", HB_TAG('s', 'l', 'n', 't')); + feature_sets.insert("width", HB_TAG('w', 'd', 't', 'h')); + feature_sets.insert("weight", HB_TAG('w', 'g', 'h', 't')); +} int32_t TextServerAdvanced::name_to_tag(const String &p_name) const { - for (int i = 0; feature_set[i].tag != 0; i++) { - if (feature_set[i].name == p_name) { - return feature_set[i].tag; - } + if (feature_sets.has(p_name)) { + return feature_sets[p_name]; } // No readable name, use tag string. @@ -707,9 +687,9 @@ int32_t TextServerAdvanced::name_to_tag(const String &p_name) const { } String TextServerAdvanced::tag_to_name(int32_t p_tag) const { - for (int i = 0; feature_set[i].tag != 0; i++) { - if (feature_set[i].tag == p_tag) { - return feature_set[i].name; + for (const KeyValue<StringName, int32_t> &E : feature_sets) { + if (E.value == p_tag) { + return E.key; } } @@ -1231,7 +1211,7 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced int error = 0; if (!library) { error = FT_Init_FreeType(&library); - ERR_FAIL_COND_V_MSG(error != 0, false, TTR("FreeType: Error initializing library:") + " '" + String(FT_Error_String(error)) + "'."); + ERR_FAIL_COND_V_MSG(error != 0, false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'."); } memset(&fd->stream, 0, sizeof(FT_StreamRec)); @@ -1249,13 +1229,7 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced if (error) { FT_Done_Face(fd->face); fd->face = nullptr; - ERR_FAIL_V_MSG(false, TTR("FreeType: Error loading font:") + " '" + String(FT_Error_String(error)) + "'."); - } - fd->hb_handle = hb_ft_font_create(fd->face, nullptr); - if (fd->hb_handle == nullptr) { - FT_Done_Face(fd->face); - fd->face = nullptr; - ERR_FAIL_V_MSG(false, TTR("HarfBuzz: Error creating FreeType font object.")); + ERR_FAIL_V_MSG(false, "FreeType: Error loading font: '" + String(FT_Error_String(error)) + "'."); } if (p_font_data->msdf) { @@ -1284,6 +1258,8 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced FT_Set_Pixel_Sizes(fd->face, 0, fd->size.x * fd->oversampling); } + fd->hb_handle = hb_ft_font_create(fd->face, nullptr); + fd->ascent = (fd->face->size->metrics.ascender / 64.0) / fd->oversampling * fd->scale; fd->descent = (-fd->face->size->metrics.descender / 64.0) / fd->oversampling * fd->scale; fd->underline_position = (-FT_MulFix(fd->face->underline_position, fd->face->size->metrics.y_scale) / 64.0) / fd->oversampling * fd->scale; @@ -1598,14 +1574,11 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced FT_Done_MM_Var(library, amaster); } #else - ERR_FAIL_V_MSG(false, TTR("FreeType: Can't load dynamic font, engine is compiled without FreeType support!"); + ERR_FAIL_V_MSG(false, "FreeType: Can't load dynamic font, engine is compiled without FreeType support!"); #endif } else { // Init bitmap font. fd->hb_handle = hb_bmp_font_create(fd, nullptr); - if (!fd->hb_handle) { - ERR_FAIL_V_MSG(false, TTR("HarfBuzz: Error creating bitmap font object.")); - } } p_font_data->cache[p_size] = fd; return true; @@ -3816,7 +3789,12 @@ bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) { gl.font_rid = sd_glyphs[i].font_rid; gl.font_size = sd_glyphs[i].font_size; gl.flags = GRAPHEME_IS_BREAK_SOFT | GRAPHEME_IS_VIRTUAL; - sd->glyphs.insert(i + sd_glyphs[i].count, gl); // Insert after. + if (sd->glyphs[i].flags & GRAPHEME_IS_RTL) { + gl.flags |= GRAPHEME_IS_RTL; + sd->glyphs.insert(i, gl); // Insert before. + } else { + sd->glyphs.insert(i + sd_glyphs[i].count, gl); // Insert after. + } // Update write pointer and size. sd_size = sd->glyphs.size(); @@ -4010,7 +3988,12 @@ bool TextServerAdvanced::shaped_text_update_justification_ops(RID p_shaped) { gl.font_rid = sd->glyphs[i].font_rid; gl.font_size = sd->glyphs[i].font_size; gl.flags = GRAPHEME_IS_SPACE | GRAPHEME_IS_VIRTUAL; - sd->glyphs.insert(i + sd->glyphs[i].count, gl); // Insert after. + if (sd->glyphs[i].flags & GRAPHEME_IS_RTL) { + gl.flags |= GRAPHEME_IS_RTL; + sd->glyphs.insert(i, gl); // Insert before. + } else { + sd->glyphs.insert(i + sd->glyphs[i].count, gl); // Insert after. + } i += sd->glyphs[i].count; continue; } @@ -4159,7 +4142,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star } } if (p_direction == HB_DIRECTION_RTL || p_direction == HB_DIRECTION_BTT) { - w[last_cluster_index].flags |= TextServer::GRAPHEME_IS_RTL; + w[last_cluster_index].flags |= GRAPHEME_IS_RTL; } if (last_cluster_valid) { w[last_cluster_index].flags |= GRAPHEME_IS_VALID; @@ -4181,6 +4164,10 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star gl.font_rid = p_fonts[p_fb_index]; gl.font_size = fs; + if (glyph_info[i].mask & HB_GLYPH_FLAG_DEFINED) { + gl.flags |= GRAPHEME_IS_CONNECTED; + } + gl.index = glyph_info[i].codepoint; if (gl.index != 0) { real_t scale = font_get_scale(f, fs); @@ -4199,9 +4186,9 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star } if (p_sd->preserve_control) { - last_cluster_valid = last_cluster_valid && ((glyph_info[i].codepoint != 0) || is_whitespace(p_sd->text[glyph_info[i].cluster]) || is_linebreak(p_sd->text[glyph_info[i].cluster])); + last_cluster_valid = last_cluster_valid && ((glyph_info[i].codepoint != 0) || (p_sd->text[glyph_info[i].cluster] == 0x0009) || (u_isblank(p_sd->text[glyph_info[i].cluster]) && (gl.advance != 0)) || (!u_isblank(p_sd->text[glyph_info[i].cluster]) && is_linebreak(p_sd->text[glyph_info[i].cluster]))); } else { - last_cluster_valid = last_cluster_valid && ((glyph_info[i].codepoint != 0) || !u_isgraph(p_sd->text[glyph_info[i].cluster])); + last_cluster_valid = last_cluster_valid && ((glyph_info[i].codepoint != 0) || (p_sd->text[glyph_info[i].cluster] == 0x0009) || (u_isblank(p_sd->text[glyph_info[i].cluster]) && (gl.advance != 0)) || (!u_isblank(p_sd->text[glyph_info[i].cluster]) && !u_isgraph(p_sd->text[glyph_info[i].cluster]))); } } if (p_direction == HB_DIRECTION_LTR || p_direction == HB_DIRECTION_TTB) { @@ -4211,7 +4198,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star } w[last_cluster_index].count = glyph_count - last_cluster_index; if (p_direction == HB_DIRECTION_RTL || p_direction == HB_DIRECTION_BTT) { - w[last_cluster_index].flags |= TextServer::GRAPHEME_IS_RTL; + w[last_cluster_index].flags |= GRAPHEME_IS_RTL; } if (last_cluster_valid) { w[last_cluster_index].flags |= GRAPHEME_IS_VALID; @@ -4633,30 +4620,76 @@ real_t TextServerAdvanced::shaped_text_get_underline_thickness(RID p_shaped) con } struct num_system_data { - String lang; + Set<String> lang; String digits; String percent_sign; String exp; }; static num_system_data num_systems[]{ - { "ar,ar_AR,ar_BH,ar_DJ,ar_EG,ar_ER,ar_IL,ar_IQ,ar_JO,ar_KM,ar_KW,ar_LB,ar_MR,ar_OM,ar_PS,ar_QA,ar_SA,ar_SD,ar_SO,ar_SS,ar_SY,ar_TD,ar_YE", U"٠١٢٣٤٥٦٧٨٩٫", U"٪", U"اس" }, - { "fa,ks,pa_Arab,ps,ug,ur_IN,ur,uz_Arab", U"۰۱۲۳۴۵۶۷۸۹٫", U"٪", U"اس" }, - { "as,bn,mni", U"০১২৩৪৫৬৭৮৯.", U"%", U"e" }, - { "mr,ne", U"०१२३४५६७८९.", U"%", U"e" }, - { "dz", U"༠༡༢༣༤༥༦༧༨༩.", U"%", U"e" }, - { "sat", U"᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙.", U"%", U"e" }, - { "my", U"၀၁၂၃၄၅၆၇၈၉.", U"%", U"e" }, - { String(), String(), String(), String() }, + { Set<String>(), U"٠١٢٣٤٥٦٧٨٩٫", U"٪", U"اس" }, + { Set<String>(), U"۰۱۲۳۴۵۶۷۸۹٫", U"٪", U"اس" }, + { Set<String>(), U"০১২৩৪৫৬৭৮৯.", U"%", U"e" }, + { Set<String>(), U"०१२३४५६७८९.", U"%", U"e" }, + { Set<String>(), U"༠༡༢༣༤༥༦༧༨༩.", U"%", U"e" }, + { Set<String>(), U"᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙.", U"%", U"e" }, + { Set<String>(), U"၀၁၂၃၄၅၆၇၈၉.", U"%", U"e" }, + { Set<String>(), String(), String(), String() }, }; +static void _insert_num_systems_lang() { + num_systems[0].lang.insert(StringName("ar")); + num_systems[0].lang.insert(StringName("ar_AR")); + num_systems[0].lang.insert(StringName("ar_BH")); + num_systems[0].lang.insert(StringName("ar_DJ")); + num_systems[0].lang.insert(StringName("ar_EG")); + num_systems[0].lang.insert(StringName("ar_ER")); + num_systems[0].lang.insert(StringName("ar_IL")); + num_systems[0].lang.insert(StringName("ar_IQ")); + num_systems[0].lang.insert(StringName("ar_JO")); + num_systems[0].lang.insert(StringName("ar_KM")); + num_systems[0].lang.insert(StringName("ar_KW")); + num_systems[0].lang.insert(StringName("ar_LB")); + num_systems[0].lang.insert(StringName("ar_MR")); + num_systems[0].lang.insert(StringName("ar_OM")); + num_systems[0].lang.insert(StringName("ar_PS")); + num_systems[0].lang.insert(StringName("ar_QA")); + num_systems[0].lang.insert(StringName("ar_SA")); + num_systems[0].lang.insert(StringName("ar_SD")); + num_systems[0].lang.insert(StringName("ar_SO")); + num_systems[0].lang.insert(StringName("ar_SS")); + num_systems[0].lang.insert(StringName("ar_SY")); + num_systems[0].lang.insert(StringName("ar_TD")); + num_systems[0].lang.insert(StringName("ar_YE")); + + num_systems[1].lang.insert(StringName("fa")); + num_systems[1].lang.insert(StringName("ks")); + num_systems[1].lang.insert(StringName("pa_Arab")); + num_systems[1].lang.insert(StringName("ug")); + num_systems[1].lang.insert(StringName("ur_IN")); + num_systems[1].lang.insert(StringName("ur")); + num_systems[1].lang.insert(StringName("uz_Arab")); + + num_systems[2].lang.insert(StringName("as")); + num_systems[2].lang.insert(StringName("bn")); + num_systems[2].lang.insert(StringName("mni")); + + num_systems[3].lang.insert(StringName("mr")); + num_systems[3].lang.insert(StringName("ne")); + + num_systems[4].lang.insert(StringName("dz")); + + num_systems[5].lang.insert(StringName("sat")); + + num_systems[6].lang.insert(StringName("my")); +} + String TextServerAdvanced::format_number(const String &p_string, const String &p_language) const { - String lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language; + const StringName lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language; String res = p_string; - for (int i = 0; num_systems[i].lang != String(); i++) { - Vector<String> langs = num_systems[i].lang.split(","); - if (langs.has(lang)) { + for (int i = 0; num_systems[i].lang.size() != 0; i++) { + if (num_systems[i].lang.has(lang)) { if (num_systems[i].digits == String()) { return p_string; } @@ -4677,12 +4710,11 @@ String TextServerAdvanced::format_number(const String &p_string, const String &p } String TextServerAdvanced::parse_number(const String &p_string, const String &p_language) const { - String lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language; + const StringName lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language; String res = p_string; - for (int i = 0; num_systems[i].lang != String(); i++) { - Vector<String> langs = num_systems[i].lang.split(","); - if (langs.has(lang)) { + for (int i = 0; num_systems[i].lang.size() != 0; i++) { + if (num_systems[i].lang.has(lang)) { if (num_systems[i].digits == String()) { return p_string; } @@ -4706,11 +4738,10 @@ String TextServerAdvanced::parse_number(const String &p_string, const String &p_ } String TextServerAdvanced::percent_sign(const String &p_language) const { - String lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language; + const StringName lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language; - for (int i = 0; num_systems[i].lang != String(); i++) { - Vector<String> langs = num_systems[i].lang.split(","); - if (langs.has(lang)) { + for (int i = 0; num_systems[i].lang.size() != 0; i++) { + if (num_systems[i].lang.has(lang)) { if (num_systems[i].percent_sign == String()) { return "%"; } @@ -4730,6 +4761,8 @@ void TextServerAdvanced::register_server() { } TextServerAdvanced::TextServerAdvanced() { + _insert_num_systems_lang(); + _insert_feature_sets(); hb_bmp_create_font_funcs(); } diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h index 5989035800..fc0e7a09a7 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -269,7 +269,6 @@ class TextServerAdvanced : public TextServer { static hb_position_t hb_bmp_get_glyph_h_advance(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, void *p_user_data); static hb_position_t hb_bmp_get_glyph_v_advance(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, void *p_user_data); static hb_position_t hb_bmp_get_glyph_h_kerning(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_left_glyph, hb_codepoint_t p_right_glyph, void *p_user_data); - static hb_position_t hb_bmp_get_glyph_v_kerning(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_left_glyph, hb_codepoint_t p_right_glyph, void *p_user_data); static hb_bool_t hb_bmp_get_glyph_v_origin(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, hb_position_t *r_x, hb_position_t *r_y, void *p_user_data); static hb_bool_t hb_bmp_get_glyph_extents(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, hb_glyph_extents_t *r_extents, void *p_user_data); static hb_bool_t hb_bmp_get_font_h_extents(hb_font_t *p_font, void *p_font_data, hb_font_extents_t *r_metrics, void *p_user_data); |