diff --git a/Assets/RTLTMPro/Scripts/Runtime/GlyphFixer.cs b/Assets/RTLTMPro/Scripts/Runtime/GlyphFixer.cs index d21a388..c4aab75 100644 --- a/Assets/RTLTMPro/Scripts/Runtime/GlyphFixer.cs +++ b/Assets/RTLTMPro/Scripts/Runtime/GlyphFixer.cs @@ -1,8 +1,11 @@ using System.Collections.Generic; -namespace RTLTMPro { - public static class GlyphFixer { - public static Dictionary EnglishToFarsiNumberMap = new Dictionary() { +namespace RTLTMPro +{ + public static class GlyphFixer + { + public static Dictionary EnglishToFarsiNumberMap = new Dictionary() + { [(char)EnglishNumbers.Zero] = (char)FarsiNumbers.Zero, [(char)EnglishNumbers.One] = (char)FarsiNumbers.One, [(char)EnglishNumbers.Two] = (char)FarsiNumbers.Two, @@ -15,7 +18,8 @@ public static class GlyphFixer { [(char)EnglishNumbers.Nine] = (char)FarsiNumbers.Nine, }; - public static Dictionary EnglishToHinduNumberMap = new Dictionary() { + public static Dictionary EnglishToHinduNumberMap = new Dictionary() + { [(char)EnglishNumbers.Zero] = (char)FarsiNumbers.Zero, [(char)EnglishNumbers.One] = (char)FarsiNumbers.One, [(char)EnglishNumbers.Two] = (char)FarsiNumbers.Two, @@ -37,18 +41,22 @@ public static class GlyphFixer { /// /// /// - public static void Fix(FastStringBuilder input, FastStringBuilder output, bool preserveNumbers, bool farsi, bool fixTextTags) { + public static void Fix(FastStringBuilder input, FastStringBuilder output, bool preserveNumbers, bool farsi, bool fixTextTags) + { FixYah(input, farsi); output.SetValue(input); - for (int i = 0; i < input.Length; i++) { + for (int i = 0; i < input.Length; i++) + { bool skipNext = false; char iChar = input.Get(i); // For special Lam Letter connections. - if (iChar == (char)GeneralLetters.Lam) { - if (i < input.Length - 1) { + if (iChar == (char)GeneralLetters.Lam) + { + if (i < input.Length - 1) + { skipNext = HandleSpecialLam(input, output, i); if (skipNext) iChar = output.Get(i); @@ -57,33 +65,42 @@ public static void Fix(FastStringBuilder input, FastStringBuilder output, bool p // We don't want to fix tatweel or zwnj character if (iChar == (int)GeneralLetters.ArabicTatweel || - iChar == (int)GeneralLetters.ZeroWidthNoJoiner) { + iChar == (int)GeneralLetters.ZeroWidthNoJoiner) + { continue; } - if (TextUtils.IsRTLCharacter(iChar)) { + if (TextUtils.IsRTLCharacter(iChar)) + { char converted = GlyphTable.Convert(iChar); - if (IsMiddleLetter(input, i)) { + if (IsMiddleLetter(input, i)) + { output.Set(i, (char)(converted + 3)); - } else if (IsFinishingLetter(input, i)) { + } else if (IsFinishingLetter(input, i)) + { output.Set(i, (char)(converted + 1)); - } else if (IsLeadingLetter(input, i)) { + } else if (IsLeadingLetter(input, i)) + { output.Set(i, (char)(converted + 2)); } } // If this letter as Lam and special Lam-Alef connection was made, We want to skip the Alef // (Lam-Alef occupies 1 space) - if (skipNext) { + if (skipNext) + { i++; } } - if (!preserveNumbers) { - if (fixTextTags) { + if (!preserveNumbers) + { + if (fixTextTags) + { FixNumbersOutsideOfTags(output, farsi); - } else { + } else + { FixNumbers(output, farsi); } } @@ -95,11 +112,15 @@ public static void Fix(FastStringBuilder input, FastStringBuilder output, bool p /// Input to prepare /// /// Prepared input in char array - public static void FixYah(FastStringBuilder text, bool farsi) { - for (int i = 0; i < text.Length; i++) { - if (farsi && text.Get(i) == (int)GeneralLetters.Ya) { + public static void FixYah(FastStringBuilder text, bool farsi) + { + for (int i = 0; i < text.Length; i++) + { + if (farsi && text.Get(i) == (int)GeneralLetters.Ya) + { text.Set(i, (char)GeneralLetters.PersianYa); - } else if (farsi == false && text.Get(i) == (int)GeneralLetters.PersianYa) { + } else if (farsi == false && text.Get(i) == (int)GeneralLetters.PersianYa) + { text.Set(i, (char)GeneralLetters.Ya); } } @@ -112,9 +133,11 @@ public static void FixYah(FastStringBuilder text, bool farsi) { /// /// Index of Lam letter /// if special connection has been made. - private static bool HandleSpecialLam(FastStringBuilder input, FastStringBuilder output, int i) { + private static bool HandleSpecialLam(FastStringBuilder input, FastStringBuilder output, int i) + { bool isFixed; - switch (input.Get(i + 1)) { + switch (input.Get(i + 1)) + { case (char)GeneralLetters.AlefMaksoor: output.Set(i, (char)0xFEF7); isFixed = true; @@ -136,7 +159,8 @@ private static bool HandleSpecialLam(FastStringBuilder input, FastStringBuilder break; } - if (isFixed) { + if (isFixed) + { output.Set(i + 1, (char)0xFFFF); } @@ -149,7 +173,8 @@ private static bool HandleSpecialLam(FastStringBuilder input, FastStringBuilder /// /// /// Converted number - public static void FixNumbers(FastStringBuilder text, bool farsi) { + public static void FixNumbers(FastStringBuilder text, bool farsi) + { text.Replace((char)EnglishNumbers.Zero, farsi ? (char)FarsiNumbers.Zero : (char)HinduNumbers.Zero); text.Replace((char)EnglishNumbers.One, farsi ? (char)FarsiNumbers.One : (char)HinduNumbers.One); text.Replace((char)EnglishNumbers.Two, farsi ? (char)FarsiNumbers.Two : (char)HinduNumbers.Two); @@ -168,18 +193,24 @@ public static void FixNumbers(FastStringBuilder text, bool farsi) { /// /// /// Text with converted numbers - public static void FixNumbersOutsideOfTags(FastStringBuilder text, bool farsi) { + public static void FixNumbersOutsideOfTags(FastStringBuilder text, bool farsi) + { var englishDigits = new HashSet(EnglishToFarsiNumberMap.Keys); - for (int i = 0; i < text.Length; i++) { + for (int i = 0; i < text.Length; i++) + { var iChar = text.Get(i); // skip valid tags - if (iChar == '<') { + if (iChar == '<') + { bool sawValidTag = false; - for (int j = i + 1; j < text.Length; j++) { + for (int j = i + 1; j < text.Length; j++) + { char jChar = text.Get(j); - if (jChar == ' ' || TextUtils.IsRTLCharacter(jChar)) { + if (jChar == ' ' || TextUtils.IsRTLCharacter(jChar)) + { break; - } else if (jChar == '>') { + } else if (jChar == '>') + { i = j; sawValidTag = true; break; @@ -189,7 +220,8 @@ public static void FixNumbersOutsideOfTags(FastStringBuilder text, bool farsi) { if (sawValidTag) continue; } - if (englishDigits.Contains(iChar)) { + if (englishDigits.Contains(iChar)) + { text.Set(i, farsi ? EnglishToFarsiNumberMap[iChar] : EnglishToHinduNumberMap[iChar]); } } @@ -199,7 +231,8 @@ public static void FixNumbersOutsideOfTags(FastStringBuilder text, bool farsi) { /// Is the letter at provided index a leading letter? /// /// if the letter is a leading letter - private static bool IsLeadingLetter(FastStringBuilder letters, int index) { + private static bool IsLeadingLetter(FastStringBuilder letters, int index) + { var currentIndexLetter = letters.Get(index); char previousIndexLetter = default; @@ -282,7 +315,8 @@ private static bool IsLeadingLetter(FastStringBuilder letters, int index) { /// Is the letter at provided index a finishing letter? /// /// if the letter is a finishing letter - private static bool IsFinishingLetter(FastStringBuilder letters, int index) { + private static bool IsFinishingLetter(FastStringBuilder letters, int index) + { char currentIndexLetter = letters.Get(index); char previousIndexLetter = default; @@ -330,7 +364,8 @@ private static bool IsFinishingLetter(FastStringBuilder letters, int index) { /// Is the letter at provided index a middle letter? /// /// if the letter is a middle letter - private static bool IsMiddleLetter(FastStringBuilder letters, int index) { + private static bool IsMiddleLetter(FastStringBuilder letters, int index) + { var currentIndexLetter = letters.Get(index); char previousIndexLetter = default; diff --git a/Assets/RTLTMPro/Scripts/Runtime/LigatureFixer.cs b/Assets/RTLTMPro/Scripts/Runtime/LigatureFixer.cs index f4398a4..41a7cec 100644 --- a/Assets/RTLTMPro/Scripts/Runtime/LigatureFixer.cs +++ b/Assets/RTLTMPro/Scripts/Runtime/LigatureFixer.cs @@ -1,18 +1,23 @@ using System.Collections.Generic; -namespace RTLTMPro { - public static class LigatureFixer { +namespace RTLTMPro +{ + public static class LigatureFixer + { private static readonly List LtrTextHolder = new List(512); private static readonly List TagTextHolder = new List(512); - private static readonly Dictionary MirroredCharsMap = new Dictionary() { + private static readonly Dictionary MirroredCharsMap = new Dictionary() + { ['('] = ')', [')'] = '(', ['»'] = '«', ['«'] = '»', }; private static readonly HashSet MirroredCharsSet = new HashSet(MirroredCharsMap.Keys); - private static void FlushBufferToOutput(List buffer, FastStringBuilder output) { - for (int j = 0; j < buffer.Count; j++) { + private static void FlushBufferToOutput(List buffer, FastStringBuilder output) + { + for (int j = 0; j < buffer.Count; j++) + { output.Append(buffer[buffer.Count - 1 - j]); } @@ -22,12 +27,14 @@ private static void FlushBufferToOutput(List buffer, FastStringBuilder out /// /// Fixes the flow of the text. /// - public static void Fix(FastStringBuilder input, FastStringBuilder output, bool farsi, bool fixTextTags, bool preserveNumbers) { + public static void Fix(FastStringBuilder input, FastStringBuilder output, bool farsi, bool fixTextTags, bool preserveNumbers) + { // Some texts like tags, English words and numbers need to be displayed in their original order. // This list keeps the characters that their order should be reserved and streams reserved texts into final letters. LtrTextHolder.Clear(); TagTextHolder.Clear(); - for (int i = input.Length - 1; i >= 0; i--) { + for (int i = input.Length - 1; i >= 0; i--) + { bool isInMiddle = i > 0 && i < input.Length - 1; bool isAtBeginning = i == 0; bool isAtEnd = i == input.Length - 1; @@ -42,58 +49,70 @@ public static void Fix(FastStringBuilder input, FastStringBuilder output, bool f if (!isAtBeginning) previousCharacter = input.Get(i - 1); - if (fixTextTags) { - if (characterAtThisIndex == '>') { + if (fixTextTags) + { + if (characterAtThisIndex == '>') + { // We need to check if it is actually the beginning of a tag. bool isValidTag = false; int nextI = i; TagTextHolder.Add(characterAtThisIndex); - for (int j = i - 1; j >= 0; j--) { + for (int j = i - 1; j >= 0; j--) + { var jChar = input.Get(j); // Tags do not have space inside - if (jChar == ' ') { + if (jChar == ' ') + { break; } // Tags do not have RTL characters inside - if (TextUtils.IsRTLCharacter(jChar)) { + if (TextUtils.IsRTLCharacter(jChar)) + { break; } TagTextHolder.Add(jChar); - if (jChar == '<') { + if (jChar == '<') + { isValidTag = true; nextI = j; break; } } - if (isValidTag) { + if (isValidTag) + { FlushBufferToOutput(LtrTextHolder, output); FlushBufferToOutput(TagTextHolder, output); i = nextI; continue; - } else { + } else + { TagTextHolder.Clear(); } } } - if (char.IsPunctuation(characterAtThisIndex) || char.IsSymbol(characterAtThisIndex)) { + if (char.IsPunctuation(characterAtThisIndex) || char.IsSymbol(characterAtThisIndex)) + { - if (MirroredCharsSet.Contains(characterAtThisIndex)) { + if (MirroredCharsSet.Contains(characterAtThisIndex)) + { // IsRTLCharacter returns false for null bool isAfterRTLCharacter = TextUtils.IsRTLCharacter(previousCharacter); bool isBeforeRTLCharacter = TextUtils.IsRTLCharacter(nextCharacter); - if (isAfterRTLCharacter || isBeforeRTLCharacter) { + if (isAfterRTLCharacter || isBeforeRTLCharacter) + { characterAtThisIndex = MirroredCharsMap[characterAtThisIndex]; } } - if (isInMiddle) { + if (isInMiddle) + { bool isAfterRTLCharacter = TextUtils.IsRTLCharacter(previousCharacter); bool isBeforeRTLCharacter = TextUtils.IsRTLCharacter(nextCharacter); bool isBeforeWhiteSpace = char.IsWhiteSpace(nextCharacter); @@ -107,22 +126,27 @@ public static void Fix(FastStringBuilder input, FastStringBuilder output, bool f isAfterWhiteSpace && isSpecialPunctuation || isBeforeWhiteSpace && isAfterRTLCharacter || isBeforeRTLCharacter && isAfterWhiteSpace || - (isBeforeRTLCharacter || isAfterRTLCharacter) && isUnderline) { + (isBeforeRTLCharacter || isAfterRTLCharacter) && isUnderline) + { FlushBufferToOutput(LtrTextHolder, output); output.Append(characterAtThisIndex); - } else { + } else + { LtrTextHolder.Add(characterAtThisIndex); } - } else if (isAtEnd) { + } else if (isAtEnd) + { LtrTextHolder.Add(characterAtThisIndex); - } else if (isAtBeginning) { + } else if (isAtBeginning) + { output.Append(characterAtThisIndex); } continue; } - if (isInMiddle) { + if (isInMiddle) + { bool isAfterEnglishChar = TextUtils.IsEnglishLetter(previousCharacter); bool isBeforeEnglishChar = TextUtils.IsEnglishLetter(nextCharacter); bool isAfterNumber = TextUtils.IsNumber(previousCharacter, preserveNumbers, farsi); @@ -134,21 +158,24 @@ public static void Fix(FastStringBuilder input, FastStringBuilder output, bool f // If the space is between numbers,symbols or English words, keep the order if (characterAtThisIndex == ' ' && (isBeforeEnglishChar || isBeforeNumber || isBeforeSymbol) && - (isAfterEnglishChar || isAfterNumber || isAfterSymbol)) { + (isAfterEnglishChar || isAfterNumber || isAfterSymbol)) + { LtrTextHolder.Add(characterAtThisIndex); continue; } } if (TextUtils.IsEnglishLetter(characterAtThisIndex) || - TextUtils.IsNumber(characterAtThisIndex, preserveNumbers, farsi)) { + TextUtils.IsNumber(characterAtThisIndex, preserveNumbers, farsi)) + { LtrTextHolder.Add(characterAtThisIndex); continue; } if (characterAtThisIndex >= (char)0xD800 && characterAtThisIndex <= (char)0xDBFF || - characterAtThisIndex >= (char)0xDC00 && characterAtThisIndex <= (char)0xDFFF) { + characterAtThisIndex >= (char)0xDC00 && characterAtThisIndex <= (char)0xDFFF) + { LtrTextHolder.Add(characterAtThisIndex); continue; } @@ -156,7 +183,8 @@ public static void Fix(FastStringBuilder input, FastStringBuilder output, bool f FlushBufferToOutput(LtrTextHolder, output); if (characterAtThisIndex != 0xFFFF && - characterAtThisIndex != (int)GeneralLetters.ZeroWidthNoJoiner) { + characterAtThisIndex != (int)GeneralLetters.ZeroWidthNoJoiner) + { output.Append(characterAtThisIndex); } } diff --git a/Assets/RTLTMPro/Scripts/Runtime/RTLSupport.cs b/Assets/RTLTMPro/Scripts/Runtime/RTLSupport.cs index 6a34ed1..602e92e 100644 --- a/Assets/RTLTMPro/Scripts/Runtime/RTLSupport.cs +++ b/Assets/RTLTMPro/Scripts/Runtime/RTLSupport.cs @@ -6,10 +6,10 @@ namespace RTLTMPro public static class RTLSupport { public const int DefaultBufferSize = 2048; - + private static FastStringBuilder inputBuilder; private static FastStringBuilder glyphFixerOutput; - + static RTLSupport() { inputBuilder = new FastStringBuilder(DefaultBufferSize); @@ -29,7 +29,7 @@ public static void FixRTL( string input, FastStringBuilder output, bool farsi = true, - bool fixTextTags = true, + bool fixTextTags = true, bool preserveNumbers = false) { inputBuilder.SetValue(input); @@ -44,11 +44,12 @@ public static void FixRTL( // Fix flow of the text and put the result in FinalLetters field LigatureFixer.Fix(glyphFixerOutput, output, farsi, fixTextTags, preserveNumbers); - if (fixTextTags) { + if (fixTextTags) + { RichTextFixer.Fix(output); } inputBuilder.Clear(); } - + } } \ No newline at end of file diff --git a/Assets/RTLTMPro/Scripts/Runtime/RTLTextMeshPro.cs b/Assets/RTLTMPro/Scripts/Runtime/RTLTextMeshPro.cs index 4fd101b..f34fb02 100644 --- a/Assets/RTLTMPro/Scripts/Runtime/RTLTextMeshPro.cs +++ b/Assets/RTLTMPro/Scripts/Runtime/RTLTextMeshPro.cs @@ -111,8 +111,7 @@ public void UpdateText() { isRightToLeftText = false; base.text = originalText; - } - else + } else { isRightToLeftText = true; base.text = GetFixedText(originalText); diff --git a/Assets/RTLTMPro/Scripts/Runtime/RichTextFixer.cs b/Assets/RTLTMPro/Scripts/Runtime/RichTextFixer.cs index eebf1a3..6e6d992 100644 --- a/Assets/RTLTMPro/Scripts/Runtime/RichTextFixer.cs +++ b/Assets/RTLTMPro/Scripts/Runtime/RichTextFixer.cs @@ -15,10 +15,10 @@ public Tag(int start, int end) End = end; } } - + private static readonly List ClosedTags = new List(64); private static readonly List ClosedTagsHash = new List(64); - + /// /// Fixes rich text tags in input string and returns the result. /// @@ -37,51 +37,50 @@ public static void Fix(FastStringBuilder text) switch (tagType) { case 1: // Opening tag - { - Tag closingTag = default; - - // Search and find the closing tag for this - bool foundClosingTag = false; - for (int j = ClosedTagsHash.Count - 1; j >= 0; j--) { - if (ClosedTagsHash[j] == hashCode) + Tag closingTag = default; + + // Search and find the closing tag for this + bool foundClosingTag = false; + for (int j = ClosedTagsHash.Count - 1; j >= 0; j--) { - closingTag = ClosedTags[j]; - foundClosingTag = true; - ClosedTags.RemoveAt(j); - ClosedTagsHash.RemoveAt(j); - break; + if (ClosedTagsHash[j] == hashCode) + { + closingTag = ClosedTags[j]; + foundClosingTag = true; + ClosedTags.RemoveAt(j); + ClosedTagsHash.RemoveAt(j); + break; + } } - } - if (foundClosingTag) - { - // NOTE: order of execution is important here + if (foundClosingTag) + { + // NOTE: order of execution is important here - int openingTagLength = tagEnd - tagStart + 1; - int closingTagLength = closingTag.End - closingTag.Start + 1; + int openingTagLength = tagEnd - tagStart + 1; + int closingTagLength = closingTag.End - closingTag.Start + 1; + + text.Reverse(tagStart, openingTagLength); + text.Reverse(closingTag.Start, closingTagLength); + } else + { + text.Reverse(tagStart, tagEnd - tagStart + 1); + } - text.Reverse(tagStart, openingTagLength); - text.Reverse(closingTag.Start, closingTagLength); + break; } - else + case 2: // Closing tag { - text.Reverse(tagStart, tagEnd - tagStart + 1); + ClosedTags.Add(new Tag(tagStart, tagEnd)); + ClosedTagsHash.Add(hashCode); + break; } - - break; - } - case 2: // Closing tag - { - ClosedTags.Add(new Tag(tagStart, tagEnd)); - ClosedTagsHash.Add(hashCode); - break; - } case 3: // Self contained tag - { - text.Reverse(tagStart, tagEnd - tagStart + 1); - break; - } + { + text.Reverse(tagStart, tagEnd - tagStart + 1); + break; + } } i = tagEnd; @@ -109,7 +108,7 @@ public static void FindTag( for (int j = i + 1; j < str.Length; j++) { char jChar = str.Get(j); - + if (calculateHashCode) { if (char.IsLetter(jChar)) @@ -119,14 +118,12 @@ public static void FindTag( if (hashCode == 0) { hashCode = jChar.GetHashCode(); - } - else + } else { hashCode = (hashCode * 397) ^ jChar.GetHashCode(); } } - } - else if (hashCode != 0) + } else if (hashCode != 0) { // We have computed the hash code. Now we reached a non letter character. We need to stop calculateHashCode = false; @@ -138,7 +135,7 @@ public static void FindTag( { break; } - + if (jChar == '>') { // Check if the tag is closing, opening or self contained