/// <summary> /// Check if a position in a code point buffer is a grapheme cluster boundary /// </summary> /// <param name="codePoints">The code points</param> /// <param name="position">The position to check</param> /// <returns></returns> public static bool IsBoundary(Slice <int> codePoints, int position) { if (codePoints.Length == 0) { return(false); } // Get the grapheme cluster class of the character on each side var a = position <= 0 ? GraphemeClusterClass.SOT : UnicodeClasses.GraphemeClusterClass(codePoints[position - 1]); var b = position < codePoints.Length ? UnicodeClasses.GraphemeClusterClass(codePoints[position]) : GraphemeClusterClass.EOT; // Rule 11 - Special handling for ZWJ in extended pictograph if (a == GraphemeClusterClass.ZWJ) { var i = position - 2; while (i >= 0 && UnicodeClasses.GraphemeClusterClass(codePoints[i]) == GraphemeClusterClass.Extend) { i--; } if (i >= 0 && UnicodeClasses.GraphemeClusterClass(codePoints[i]) == GraphemeClusterClass.ExtPict) { a = GraphemeClusterClass.ExtPictZwg; } } // Special handling for regional indicator // Rule 12 and 13 if (a == GraphemeClusterClass.Regional_Indicator) { // Count how many int count = 0; for (int i = position - 1; i > 0; i--) { if (UnicodeClasses.GraphemeClusterClass(codePoints[i - 1]) != GraphemeClusterClass.Regional_Indicator) { break; } count++; } // If odd, switch from RI to Any if ((count % 2) != 0) { a = GraphemeClusterClass.Any; } } return(pairTable[(int)b][(int)a] != 0); }