|
@@ -0,0 +1,199 @@
|
|
|
+using System;
|
|
|
+using System.Collections.Generic;
|
|
|
+using System.Text.RegularExpressions;
|
|
|
+
|
|
|
+namespace Avalonia.Media
|
|
|
+{
|
|
|
+ /// <summary>
|
|
|
+ /// The <see cref="UnicodeRange"/> descripes a set of Unicode characters.
|
|
|
+ /// </summary>
|
|
|
+ public readonly struct UnicodeRange
|
|
|
+ {
|
|
|
+ public static UnicodeRange Default = Parse("0-10FFFD");
|
|
|
+
|
|
|
+ private readonly UnicodeRangeSegment _single;
|
|
|
+ private readonly IReadOnlyList<UnicodeRangeSegment> _segments = null;
|
|
|
+
|
|
|
+ public UnicodeRange(int start, int end)
|
|
|
+ {
|
|
|
+ _single = new UnicodeRangeSegment(start, end);
|
|
|
+ }
|
|
|
+
|
|
|
+ public UnicodeRange(UnicodeRangeSegment single)
|
|
|
+ {
|
|
|
+ _single = single;
|
|
|
+ }
|
|
|
+
|
|
|
+ public UnicodeRange(IReadOnlyList<UnicodeRangeSegment> segments)
|
|
|
+ {
|
|
|
+ if(segments is null || segments.Count == 0)
|
|
|
+ {
|
|
|
+ throw new ArgumentException(nameof(segments));
|
|
|
+ }
|
|
|
+
|
|
|
+ _single = segments[0];
|
|
|
+ _segments = segments;
|
|
|
+ }
|
|
|
+
|
|
|
+ internal UnicodeRangeSegment Single => _single;
|
|
|
+
|
|
|
+ internal IReadOnlyList<UnicodeRangeSegment> Segments => _segments;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Determines if given value is inside the range.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="value">The value to verify.</param>
|
|
|
+ /// <returns>
|
|
|
+ /// <c>true</c> If given value is inside the range, <c>false</c> otherwise.
|
|
|
+ /// </returns>
|
|
|
+ public bool IsInRange(int value)
|
|
|
+ {
|
|
|
+ if(_segments is null)
|
|
|
+ {
|
|
|
+ return _single.IsInRange(value);
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach(var segment in _segments)
|
|
|
+ {
|
|
|
+ if (segment.IsInRange(value))
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Parses a <see cref="UnicodeRange"/>.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="s">The string to parse.</param>
|
|
|
+ /// <returns>The parsed <see cref="UnicodeRange"/>.</returns>
|
|
|
+ /// <exception cref="FormatException"></exception>
|
|
|
+ public static UnicodeRange Parse(string s)
|
|
|
+ {
|
|
|
+ if (string.IsNullOrEmpty(s))
|
|
|
+ {
|
|
|
+ throw new FormatException("Could not parse specified Unicode range.");
|
|
|
+ }
|
|
|
+
|
|
|
+ var parts = s.Split(',');
|
|
|
+
|
|
|
+ var length = parts.Length;
|
|
|
+
|
|
|
+ if(length == 0)
|
|
|
+ {
|
|
|
+ throw new FormatException("Could not parse specified Unicode range.");
|
|
|
+ }
|
|
|
+
|
|
|
+ if(length == 1)
|
|
|
+ {
|
|
|
+ return new UnicodeRange(UnicodeRangeSegment.Parse(parts[0]));
|
|
|
+ }
|
|
|
+
|
|
|
+ var segments = new UnicodeRangeSegment[length];
|
|
|
+
|
|
|
+ for (int i = 0; i < length; i++)
|
|
|
+ {
|
|
|
+ segments[i] = UnicodeRangeSegment.Parse(parts[i].Trim());
|
|
|
+ }
|
|
|
+
|
|
|
+ return new UnicodeRange(segments);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public readonly struct UnicodeRangeSegment
|
|
|
+ {
|
|
|
+ private static Regex s_regex = new Regex(@"^(?:[uU]\+)?(?:([0-9a-fA-F](?:[0-9a-fA-F?]{1,5})?))$");
|
|
|
+
|
|
|
+ public UnicodeRangeSegment(int start, int end)
|
|
|
+ {
|
|
|
+ Start = start;
|
|
|
+ End = end;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Get the start of the segment.
|
|
|
+ /// </summary>
|
|
|
+ public int Start { get; }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Get the end of the segment.
|
|
|
+ /// </summary>
|
|
|
+ public int End { get; }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Determines if given value is inside the range segment.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="value">The value to verify.</param>
|
|
|
+ /// <returns>
|
|
|
+ /// <c>true</c> If given value is inside the range segment, <c>false</c> otherwise.
|
|
|
+ /// </returns>
|
|
|
+ public bool IsInRange(int value)
|
|
|
+ {
|
|
|
+ return value - Start <= End - Start;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Parses a <see cref="UnicodeRangeSegment"/>.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="s">The string to parse.</param>
|
|
|
+ /// <returns>The parsed <see cref="UnicodeRangeSegment"/>.</returns>
|
|
|
+ /// <exception cref="FormatException"></exception>
|
|
|
+ public static UnicodeRangeSegment Parse(string s)
|
|
|
+ {
|
|
|
+ if (string.IsNullOrEmpty(s))
|
|
|
+ {
|
|
|
+ throw new FormatException("Could not parse specified Unicode range segment.");
|
|
|
+ }
|
|
|
+
|
|
|
+ var parts = s.Split('-');
|
|
|
+
|
|
|
+ int start, end;
|
|
|
+
|
|
|
+ switch (parts.Length)
|
|
|
+ {
|
|
|
+ case 1:
|
|
|
+ {
|
|
|
+ //e.g. U+20, U+3F U+30??
|
|
|
+ var single = s_regex.Match(parts[0]);
|
|
|
+
|
|
|
+ if (!single.Success)
|
|
|
+ {
|
|
|
+ throw new FormatException("Could not parse specified Unicode range segment.");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!single.Value.Contains("?"))
|
|
|
+ {
|
|
|
+ start = int.Parse(single.Groups[1].Value, System.Globalization.NumberStyles.HexNumber);
|
|
|
+ end = start;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ start = int.Parse(single.Groups[1].Value.Replace('?', '0'), System.Globalization.NumberStyles.HexNumber);
|
|
|
+ end = int.Parse(single.Groups[1].Value.Replace('?', 'F'), System.Globalization.NumberStyles.HexNumber);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case 2:
|
|
|
+ {
|
|
|
+ var first = s_regex.Match(parts[0]);
|
|
|
+ var second = s_regex.Match(parts[1]);
|
|
|
+
|
|
|
+ if (!first.Success || !second.Success)
|
|
|
+ {
|
|
|
+ throw new FormatException("Could not parse specified Unicode range segment.");
|
|
|
+ }
|
|
|
+
|
|
|
+ start = int.Parse(first.Groups[1].Value, System.Globalization.NumberStyles.HexNumber);
|
|
|
+ end = int.Parse(second.Groups[1].Value, System.Globalization.NumberStyles.HexNumber);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ throw new FormatException("Could not parse specified Unicode range segment.");
|
|
|
+ }
|
|
|
+
|
|
|
+ return new UnicodeRangeSegment(start, end);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|