diff --git a/modules/text_server_adv/script_iterator.cpp b/modules/text_server_adv/script_iterator.cpp index 41ef6f1349a..2080fa66ed3 100644 --- a/modules/text_server_adv/script_iterator.cpp +++ b/modules/text_server_adv/script_iterator.cpp @@ -56,6 +56,11 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length UScriptCode script_code; }; + struct EmojiSubrunEntry { + int start; + int end; + }; + if (p_start >= p_length) { p_start = p_length - 1; } @@ -65,8 +70,12 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length } int paren_size = PAREN_STACK_DEPTH; - ParenStackEntry starter_stack[PAREN_STACK_DEPTH]; - ParenStackEntry *paren_stack = starter_stack; + ParenStackEntry starter_paren_stack[PAREN_STACK_DEPTH]; + ParenStackEntry *paren_stack = starter_paren_stack; + + int emoji_size = EMOJI_STACK_DEPTH; + EmojiSubrunEntry starter_emoji_stack[EMOJI_STACK_DEPTH]; + EmojiSubrunEntry *emoji_stack = starter_emoji_stack; int script_start; int script_end = p_start; @@ -78,19 +87,38 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length do { script_code = USCRIPT_COMMON; + int emoji_sp = -1; + bool emoji_run = false; for (script_start = script_end; script_end < p_length; script_end++) { UChar32 ch = str[script_end]; UChar32 n = (script_end + 1 < p_length) ? str[script_end + 1] : 0; + if (is_emoji(ch, n)) { + if (!emoji_run) { + emoji_run = true; + emoji_sp++; + if (unlikely(emoji_sp >= emoji_size)) { + emoji_size += EMOJI_STACK_DEPTH; + if (emoji_stack == starter_emoji_stack) { + emoji_stack = static_cast(memalloc(emoji_size * sizeof(EmojiSubrunEntry))); + } else { + emoji_stack = static_cast(memrealloc(emoji_stack, emoji_size * sizeof(EmojiSubrunEntry))); + } + } + emoji_stack[emoji_sp].start = script_end; + emoji_stack[emoji_sp].end = script_end; + } + } else if (emoji_run && ch != ZERO_WIDTH_JOINER && ch != VARIATION_SELECTOR_16 && !(u_hasBinaryProperty(ch, UCHAR_EXTENDED_PICTOGRAPHIC) && n != VARIATION_SELECTOR_15)) { + emoji_run = false; + emoji_stack[emoji_sp].end = script_end; + } + UScriptCode sc = uscript_getScript(ch, &err); if (U_FAILURE(err)) { - if (paren_stack != starter_stack) { + if (paren_stack != starter_paren_stack) { memfree(paren_stack); } ERR_FAIL_MSG(u_errorName(err)); } - if (is_emoji(ch, n)) { - sc = USCRIPT_SYMBOLS_EMOJI; - } if (u_getIntPropertyValue(ch, UCHAR_BIDI_PAIRED_BRACKET_TYPE) != U_BPT_NONE) { if (u_getIntPropertyValue(ch, UCHAR_BIDI_PAIRED_BRACKET_TYPE) == U_BPT_OPEN) { @@ -99,7 +127,7 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length 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; - if (paren_stack == starter_stack) { + if (paren_stack == starter_paren_stack) { paren_stack = static_cast(memalloc(paren_size * sizeof(ParenStackEntry))); } else { paren_stack = static_cast(memrealloc(paren_stack, paren_size * sizeof(ParenStackEntry))); @@ -122,11 +150,7 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length } } - if (script_code == USCRIPT_SYMBOLS_EMOJI && script_code != sc) { - if (ch == VARIATION_SELECTOR_15 || n == VARIATION_SELECTOR_15 || !(is_emoji(ch, n) || ch == ZERO_WIDTH_JOINER || ch == VARIATION_SELECTOR_16 || u_hasBinaryProperty(ch, UCHAR_EXTENDED_PICTOGRAPHIC))) { - break; - } - } else if (same_script(script_code, sc)) { + 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. @@ -145,16 +169,40 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length break; } } + if (emoji_run) { + emoji_stack[emoji_sp].end = script_end; + } - ScriptRange rng; - rng.script = hb_icu_script_to_script(script_code); - rng.start = script_start; - rng.end = script_end; + for (int sub = 0; sub <= emoji_sp; sub++) { + if (emoji_stack[sub].start > script_start) { + ScriptRange rng; + rng.script = hb_icu_script_to_script(script_code); + rng.start = script_start; + rng.end = emoji_stack[sub].start; + script_ranges.push_back(rng); + } + ScriptRange rng; + rng.script = (hb_script_t)HB_TAG('Z', 's', 'y', 'e'); + rng.start = emoji_stack[sub].start; + rng.end = emoji_stack[sub].end; + script_ranges.push_back(rng); - script_ranges.push_back(rng); + script_start = emoji_stack[sub].end; + } + if (script_start != script_end) { + ScriptRange rng; + rng.script = hb_icu_script_to_script(script_code); + rng.start = script_start; + rng.end = script_end; + script_ranges.push_back(rng); + } + + if (emoji_stack != starter_emoji_stack) { + memfree(emoji_stack); + } } while (script_end < p_length); - if (paren_stack != starter_stack) { + if (paren_stack != starter_paren_stack) { memfree(paren_stack); } } diff --git a/modules/text_server_adv/script_iterator.h b/modules/text_server_adv/script_iterator.h index 8a1de6f30e0..5d140b5a262 100644 --- a/modules/text_server_adv/script_iterator.h +++ b/modules/text_server_adv/script_iterator.h @@ -58,6 +58,7 @@ using namespace godot; class ScriptIterator { static const int PAREN_STACK_DEPTH = 128; + static const int EMOJI_STACK_DEPTH = 32; public: struct ScriptRange {