#region Copyright RenGuiYou. All rights reserved. //===================================================== // NeatlyFrameWork // Author: RenGuiyou // Feedback: mailto:750539605@qq.com // des: 字符解析参考:https://github.com/Konash/arabic-support-unity //===================================================== #endregion using System; using System.Collections.Generic; using System.Text; namespace Neatly.UI { internal class ArabicFixer { public static string Fix(string text) { return !CheckRTL(text) ? text : QuickFix(text); } public static string QuickFix(string text) { string[] sourceArray = text.Split(ConstChar.NEW_LINE); if (sourceArray.Length <= 1) { return FixLine(text); } var sb = UIStringBuilder.Create(); sb.Append(FixLine(sourceArray[0])); for (int i = 1; i < sourceArray.Length; i++) { sb.Append(Environment.NewLine); sb.Append(FixLine(sourceArray[i])); } return UIStringBuilder.GetStringAndRelease(sb); } public static void QuickFix1(List sourceArray) { var lastIndex = 0; for (var i = 0; i < sourceArray.Count; i++) { var item = sourceArray[i]; if (item.isNewLine) { TranslateList(sourceArray, lastIndex, i - 1); lastIndex = i + 1; } else { var value = FixLine(item.text); item.text = value; sourceArray[i] = item; } } } private static void TranslateList(IList list, int start, int end) { if(start == end || end < 0) return; var count = (start + end + 1) / 2; for (var i = start; i < count; i++) { var endIndex = end - (i - start); var temp = list[i]; list[i] = list[endIndex]; list[endIndex] = temp; } } public static string FixLine(string text) { char[] sourceArray = text.ToCharArray(); char[] targetArray = text.ToCharArray(); for (int i = 0; i < sourceArray.Length; i++) { sourceArray[i] = ArabicTable.ArabicMapper.Convert(sourceArray[i]); } for (int i = 0; i < sourceArray.Length; i++) { bool flag = false; if (sourceArray[i] == 'ﻝ') { if (i < sourceArray.Length - 1) { if (sourceArray[i + 1] == 'ﺇ') { sourceArray[i] = 'ﻷ'; targetArray[i + 1] = '￿'; flag = true; } else if (sourceArray[i + 1] == 'ﺍ') { sourceArray[i] = 'ﻹ'; targetArray[i + 1] = '￿'; flag = true; } else if (sourceArray[i + 1] == 'ﺃ') { sourceArray[i] = 'ﻵ'; targetArray[i + 1] = '￿'; flag = true; } else if (sourceArray[i + 1] == 'ﺁ') { sourceArray[i] = 'ﻳ'; targetArray[i + 1] = '￿'; flag = true; } } } if (!IsIgnoredCharacter(sourceArray[i])) { if (IsMiddleLetter(sourceArray, i)) { targetArray[i] = (char)(sourceArray[i] + '\u0003'); } else if (IsFinishingLetter(sourceArray, i)) { targetArray[i] = (char)(sourceArray[i] + '\u0001'); } else if (IsLeadingLetter(sourceArray, i)) { targetArray[i] = (char)(sourceArray[i] + '\u0002'); } } if (flag) { i++; } } StringBuilder arabList = UIStringBuilder.Create(); StringBuilder ltrList = UIStringBuilder.Create(); for (int i = targetArray.Length - 1; i >= 0; i--) { char c = targetArray[i]; bool isRtlPunctuation = IsRTLPunctuation(ref targetArray, i); if (IsRTL(c) || isRtlPunctuation || (c == ' ' && IsRTLSpace(ref targetArray, i))) { if (ltrList.Length > 0) { for (int j = 0; j < ltrList.Length; j++) { arabList.Append(ltrList[ltrList.Length - 1 - j]); } ltrList.Length = 0; } } if (c == ' ') { if (IsRTLSpace(ref targetArray, i)) { arabList.Append(c); } else { ltrList.Append(c); } } else if (IsLTR(c)) { if (c == '“') { arabList.Append('”'); } else if (c == '”') { arabList.Append('“'); } else if (c == '(') { arabList.Append(')'); } else if (c == ')') { arabList.Append('('); } else if (c == '(') { arabList.Append(')'); } else if (c == ')') { arabList.Append('('); } else if (c == '[') { arabList.Append(']'); } else if (c == ']') { arabList.Append('['); } else if (c == '【') { arabList.Append('】'); } else if (c == '】') { arabList.Append('【'); } else if (c == '{') { arabList.Append('}'); } else if (c == '}') { arabList.Append('{'); } else if (c == '≤') { arabList.Append('≥'); } else if (c == '≥') { arabList.Append('≤'); } else if (c == '<') { arabList.Append('>'); } else if (c == '>') { arabList.Append('<'); } else if (isRtlPunctuation) { arabList.Append(c); } else { ltrList.Append(c); } } else if (IsRTL(c)) { //为真主加个皇冠... if ((i - 1) >= 0 && c == 'ﻠ' && targetArray[i - 1] == 'ﻟ') { arabList.Append('ّ'); } arabList.Append(c); } else { ltrList.Append(c); } } if (ltrList.Length > 0) { for (int j = 0; j < ltrList.Length; j++) { arabList.Append(ltrList[ltrList.Length - 1 - j]); } ltrList.Length = 0; } StringBuilder sb = UIStringBuilder.Create(); for (int i = 0; i < arabList.Length; i++) { if (arabList[i].Equals('￿')) continue; sb.Append(arabList[i]); } UIStringBuilder.Release(arabList); UIStringBuilder.Release(ltrList); return UIStringBuilder.GetStringAndRelease(sb); } internal static bool IsRTLPunctuation(ref char[] sb, int index) { bool maybeLtrPunctuation = PossibleLtrPunctuation(sb[index]); if (char.IsPunctuation(sb[index]) || maybeLtrPunctuation) { if (index > 0 && sb.Length > index + 1) { if (maybeLtrPunctuation && IsLtrNoPuncSpace(sb[index - 1]) && IsLtrNoPuncSpace(sb[index + 1])) { return false; } } return true; } return false; } private static readonly char[] TypePunctuation = { ',', ',', '.', ':', ':', '/', '+', '-', '*', '=' }; /// /// 参与符号判断 /// /// /// internal static bool PossibleLtrPunctuation(char c) { for (int i = 0; i < TypePunctuation.Length; i++) { if (c == TypePunctuation[i]) { return true; } } return false; } internal static bool IsIgnoredCharacter(char ch) { bool flag = char.IsPunctuation(ch); bool flag2 = char.IsNumber(ch); bool flag3 = char.IsLower(ch); bool flag4 = char.IsUpper(ch); bool flag5 = char.IsSymbol(ch); bool flag6 = ch == 'ﭖ' || ch == 'ﭺ' || ch == 'ﮊ' || ch == 'ﮒ'; bool flag7 = (ch <= '' && ch >= 'ﹰ') || flag6; return flag || flag2 || flag3 || flag4 || flag5 || !flag7 || ch == 'a' || ch == '>' || ch == '<' || ch == '؛'; } internal static bool IsLeadingLetter(char[] letters, int index) { return (index == 0 || letters[index - 1] == ' ' || letters[index - 1] == '*' || letters[index - 1] == 'A' || char.IsPunctuation(letters[index - 1]) || letters[index - 1] == '>' || letters[index - 1] == '<' || letters[index - 1] == 'ﺍ' || letters[index - 1] == 'ﺩ' || letters[index - 1] == 'ﺫ' || letters[index - 1] == 'ﺭ' || letters[index - 1] == 'ﺯ' || letters[index - 1] == 'ﮊ' || letters[index - 1] == 'ﻯ' || letters[index - 1] == 'ﻭ' || letters[index - 1] == 'ﺁ' || letters[index - 1] == 'ﺃ' || letters[index - 1] == 'ﺇ' || letters[index - 1] == 'ﺅ' || letters[index - 1] == 'ﺀ') &&//这里加入了对ﺀ的解析 letters[index] != 'ﺅ' && letters[index] != ' ' && letters[index] != 'ﺩ' && letters[index] != 'ﺫ' && letters[index] != 'ﺭ' && letters[index] != 'ﺯ' && letters[index] != 'ﮊ' && letters[index] != 'ﺍ' && letters[index] != 'ﺃ' && letters[index] != 'ﺇ' && letters[index] != 'ﻭ' && letters[index] != 'ﺀ' && letters[index] != 'ﺁ' && index < letters.Length - 1 && letters[index + 1] != ' ' && !char.IsPunctuation(letters[index + 1]) && letters[index + 1] != 'ﺀ'; } internal static bool IsFinishingLetter(char[] letters, int index) { return index != 0 && letters[index - 1] != ' ' && letters[index - 1] != '*' && letters[index - 1] != 'A' && letters[index - 1] != 'ﺩ' && letters[index - 1] != 'ﺫ' && letters[index - 1] != 'ﺭ' && letters[index - 1] != 'ﺯ' && letters[index - 1] != 'ﮊ' && letters[index - 1] != 'ﻯ' && letters[index - 1] != 'ﻭ' && letters[index - 1] != 'ﺍ' && letters[index - 1] != 'ﺁ' && letters[index - 1] != 'ﺃ' && letters[index - 1] != 'ﺇ' && letters[index - 1] != 'ﺅ' && letters[index - 1] != 'ﺀ' && !char.IsPunctuation(letters[index - 1]) && letters[index - 1] != '>' && letters[index - 1] != '<' && letters[index] != ' ' && index < letters.Length && letters[index] != 'ﺀ'; } internal static bool IsMiddleLetter(char[] letters, int index) { if (index == 0 || letters[index] == ' ' || letters[index] == 'ﺍ' || letters[index] == 'ﺩ' || letters[index] == 'ﺫ' || letters[index] == 'ﺭ' || letters[index] == 'ﺯ' || letters[index] == 'ﮊ' || letters[index] == 'ﻯ' || letters[index] == 'ﻭ' || letters[index] == 'ﺁ' || letters[index] == 'ﺃ' || letters[index] == 'ﺇ' || letters[index] == 'ﺅ' || letters[index] == 'ﺀ' || letters[index - 1] == 'ﺍ' || letters[index - 1] == 'ﺩ' || letters[index - 1] == 'ﺫ' || letters[index - 1] == 'ﺭ' || letters[index - 1] == 'ﺯ' || letters[index - 1] == 'ﮊ' || letters[index - 1] == 'ﻯ' || letters[index - 1] == 'ﻭ' || letters[index - 1] == 'ﺁ' || letters[index - 1] == 'ﺃ' || letters[index - 1] == 'ﺇ' || letters[index - 1] == 'ﺅ' || letters[index - 1] == 'ﺀ' || letters[index - 1] == '>' || letters[index - 1] == '<' || letters[index - 1] == ' ' || letters[index - 1] == '*' || char.IsPunctuation(letters[index - 1]) || index >= letters.Length - 1 || letters[index + 1] == ' ' || letters[index + 1] == '\r' || letters[index + 1] == 'A' || letters[index + 1] == '>' || letters[index + 1] == '>' || letters[index + 1] == 'ﺀ') return false; try { return !char.IsPunctuation(letters[index + 1]); } catch { return false; } } /// /// 是否需要右向左排 /// public static bool IsRTL(int num) { return ((num >= 0x590) && (num <= 0x5ff)) || ((num >= 0x600) && (num <= 0x6ff)) || ((num >= 0x750) && (num <= 0x77f)) || ((num >= 0x8a0) && (num <= 0x8ff)) || ((num >= 0xfb50) && (num <= 0xfdff)) || ((num >= 0xfe70) && (num <= 0xfeff)) || ((num >= 0x1ee00) && (num <= 0x1eeff)) || ((num >= 0x10e60) && (num <= 0x10e7f)); } /// /// 从左向右 /// /// /// public static bool IsLTR(char c) { return !IsRTL(c); } /// /// 检测是否有RTL语句 /// /// /// public static bool CheckRTL(string str) { for (int i = 0; i < str.Length; i++) { if (IsRTL(str[i])) { return true; } } return false; } private static StringBuilder m_word = new StringBuilder(); /// /// 空格是否RTL /// /// public static bool IsRTLSpace(ref char[] sb, int index) { if (index > 0 && index < sb.Length - 1) { //判断整个单词是否阿语 //获取左右两边单词. m_word.Length = 0; for (int i = index - 1; i >= 0; i--) { if (sb[i] == ' ' || char.IsPunctuation(sb[i])) break; m_word.Append(sb[i]); } if (m_word.ToString().Equals("x") || CheckRTL(m_word.ToString())) return true; m_word.Length = 0; //判断空格左右是否是LTR for (int i = index + 1; i < sb.Length; i++) { if (sb[i] == ' ' || char.IsPunctuation(sb[i])) break; m_word.Append(sb[i]); } if (m_word.ToString().Equals("x") || CheckRTL(m_word.ToString())) return true; } return false; } /// /// 右向左或者符号 /// /// public static bool IsRtlOrPunc(char c) { if (IsRTL(c)) return true; if (char.IsPunctuation(c)) return true; if (c == ' ') return true; return false; } /// /// 非阿语非字符非空格 /// /// /// public static bool IsLtrNoPuncSpace(char c) { if (IsRTL(c) || c == ' ' || char.IsPunctuation(c) || PossibleLtrPunctuation(c)) { return false; } return true; } } internal class ArabicTable { private static Dictionary m_Mapping; private static ArabicTable m_Mapper; internal static ArabicTable ArabicMapper { get { return m_Mapper ?? (m_Mapper = new ArabicTable()); } } private ArabicTable() { m_Mapping = new Dictionary(); m_Mapping.Add((char)1569, (char)65152); m_Mapping.Add((char)1575, (char)65165); m_Mapping.Add((char)1571, (char)65155); m_Mapping.Add((char)1572, (char)65157); m_Mapping.Add((char)1573, (char)65159); m_Mapping.Add((char)1609, (char)65263); m_Mapping.Add((char)1574, (char)65161); m_Mapping.Add((char)1576, (char)65167); m_Mapping.Add((char)1578, (char)65173); m_Mapping.Add((char)1579, (char)65177); m_Mapping.Add((char)1580, (char)65181); m_Mapping.Add((char)1581, (char)65185); m_Mapping.Add((char)1582, (char)65189); m_Mapping.Add((char)1583, (char)65193); m_Mapping.Add((char)1584, (char)65195); m_Mapping.Add((char)1585, (char)65197); m_Mapping.Add((char)1586, (char)65199); m_Mapping.Add((char)1587, (char)65201); m_Mapping.Add((char)1588, (char)65205); m_Mapping.Add((char)1589, (char)65209); m_Mapping.Add((char)1590, (char)65213); m_Mapping.Add((char)1591, (char)65217); m_Mapping.Add((char)1592, (char)65221); m_Mapping.Add((char)1593, (char)65225); m_Mapping.Add((char)1594, (char)65229); m_Mapping.Add((char)1601, (char)65233); m_Mapping.Add((char)1602, (char)65237); m_Mapping.Add((char)1603, (char)65241); m_Mapping.Add((char)1604, (char)65245); m_Mapping.Add((char)1605, (char)65249); m_Mapping.Add((char)1606, (char)65253); m_Mapping.Add((char)1607, (char)65257); m_Mapping.Add((char)1608, (char)65261); m_Mapping.Add((char)1610, (char)65265); m_Mapping.Add((char)1570, (char)65153); m_Mapping.Add((char)1577, (char)65171); m_Mapping.Add((char)1662, (char)64342); m_Mapping.Add((char)1670, (char)64378); m_Mapping.Add((char)1688, (char)64394); m_Mapping.Add((char)1711, (char)64402); } internal char Convert(char sourceChar) { return m_Mapping.ContainsKey(sourceChar) ? m_Mapping[sourceChar] : sourceChar; } } }