using System.Globalization; using Wcwidth; namespace Terminal.Gui; /// Extends to support TUI text manipulation. public static class RuneExtensions { /// Maximum Unicode code point. public static int MaxUnicodeCodePoint = 0x10FFFF; /// Reports if the provided array of bytes can be encoded as UTF-8. /// The byte array to probe. /// true if is valid; otherwise, false. public static bool CanBeEncodedAsRune (byte [] buffer) { string str = Encoding.Unicode.GetString (buffer); foreach (Rune rune in str.EnumerateRunes ()) { if (rune == Rune.ReplacementChar) { return false; } } return true; } /// Attempts to decode the rune as a surrogate pair to UTF-16. /// This is a Terminal.Gui extension method to to support TUI text manipulation. /// The rune to decode. /// The chars if the rune is a surrogate pair. Null otherwise. /// if the rune is a valid surrogate pair; otherwise. public static bool DecodeSurrogatePair (this Rune rune, out char [] chars) { if (rune.IsSurrogatePair ()) { chars = rune.ToString ().ToCharArray (); return true; } chars = null; return false; } /// Writes into the destination buffer starting at offset the UTF8 encoded version of the rune. /// This is a Terminal.Gui extension method to to support TUI text manipulation. /// The rune to encode. /// The destination buffer. /// Starting offset to look into. /// Number of bytes valid in the buffer, or -1 to make it the length of the buffer. /// he number of bytes written into the destination buffer. public static int Encode (this Rune rune, byte [] dest, int start = 0, int count = -1) { byte [] bytes = Encoding.UTF8.GetBytes (rune.ToString ()); var length = 0; for (var i = 0; i < (count == -1 ? bytes.Length : count); i++) { if (bytes [i] == 0) { break; } dest [start + i] = bytes [i]; length++; } return length; } /// Attempts to encode (as UTF-16) a surrogate pair. /// The high surrogate code point. /// The low surrogate code point. /// The encoded rune. /// if the encoding succeeded; otherwise. public static bool EncodeSurrogatePair (char highSurrogate, char lowSurrogate, out Rune result) { result = default (Rune); if (char.IsSurrogatePair (highSurrogate, lowSurrogate)) { result = (Rune)char.ConvertToUtf32 (highSurrogate, lowSurrogate); return true; } return false; } /// Gets the number of columns the rune occupies in the terminal. /// This is a Terminal.Gui extension method to to support TUI text manipulation. /// The rune to measure. /// /// The number of columns required to fit the rune, 0 if the argument is the null character, or -1 if the value is /// not printable, otherwise the number of columns that the rune occupies. /// public static int GetColumns (this Rune rune) { return UnicodeCalculator.GetWidth (rune); } /// Get number of bytes required to encode the rune, based on the provided encoding. /// This is a Terminal.Gui extension method to to support TUI text manipulation. /// The rune to probe. /// The encoding used; the default is UTF8. /// The number of bytes required. public static int GetEncodingLength (this Rune rune, Encoding encoding = null) { encoding ??= Encoding.UTF8; byte [] bytes = encoding.GetBytes (rune.ToString ().ToCharArray ()); var offset = 0; if (bytes [^1] == 0) { offset++; } return bytes.Length - offset; } /// Returns if the rune is a combining character. /// This is a Terminal.Gui extension method to to support TUI text manipulation. /// /// public static bool IsCombiningMark (this Rune rune) { UnicodeCategory category = Rune.GetUnicodeCategory (rune); return Rune.GetUnicodeCategory (rune) == UnicodeCategory.NonSpacingMark || category == UnicodeCategory.SpacingCombiningMark || category == UnicodeCategory.EnclosingMark; } /// Reports whether a rune is a surrogate code point. /// This is a Terminal.Gui extension method to to support TUI text manipulation. /// The rune to probe. /// if the rune is a surrogate code point; otherwise. public static bool IsSurrogatePair (this Rune rune) { return char.IsSurrogatePair (rune.ToString (), 0); } /// /// Ensures the rune is not a control character and can be displayed by translating characters below 0x20 to /// equivalent, printable, Unicode chars. /// /// This is a Terminal.Gui extension method to to support TUI text manipulation. /// /// public static Rune MakePrintable (this Rune rune) { return Rune.IsControl (rune) ? new Rune (rune.Value + 0x2400) : rune; } }