Browse Source
* Ability to configure font features * Minor adjustments --------- Co-authored-by: Herman Kirshin <herman.kirshin@jetbrains.com> Co-authored-by: Benedikt Stebner <Gillibald@users.noreply.github.com>pull/14430/head
committed by
GitHub
18 changed files with 479 additions and 19 deletions
@ -0,0 +1,153 @@ |
|||
using System.Globalization; |
|||
using System.Text; |
|||
using System.Text.RegularExpressions; |
|||
|
|||
namespace Avalonia.Media; |
|||
|
|||
/// <summary>
|
|||
/// Font feature
|
|||
/// </summary>
|
|||
public record FontFeature |
|||
{ |
|||
private const int DefaultValue = 1; |
|||
private const int InfinityEnd = -1; |
|||
|
|||
private static readonly Regex s_featureRegex = new Regex( |
|||
@"^\s*(?<Value>[+-])?\s*(?<Tag>\w{4})\s*(\[\s*(?<Start>\d+)?(\s*(?<Separator>:)\s*)?(?<End>\d+)?\s*\])?\s*(?(Value)()|(=\s*(?<Value>\d+|on|off)))?\s*$", |
|||
RegexOptions.Compiled | RegexOptions.ExplicitCapture); |
|||
|
|||
/// <summary>Gets or sets the tag.</summary>
|
|||
public string Tag |
|||
{ |
|||
get; |
|||
init; |
|||
} |
|||
|
|||
/// <summary>Gets or sets the value.</summary>
|
|||
public int Value |
|||
{ |
|||
get; |
|||
init; |
|||
} |
|||
|
|||
/// <summary>Gets or sets the start.</summary>
|
|||
public int Start |
|||
{ |
|||
get; |
|||
init; |
|||
} |
|||
|
|||
/// <summary>Gets or sets the end.</summary>
|
|||
public int End |
|||
{ |
|||
get; |
|||
init; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates an instance of FontFeature.
|
|||
/// </summary>
|
|||
public FontFeature() |
|||
{ |
|||
Tag = string.Empty; |
|||
Value = DefaultValue; |
|||
Start = 0; |
|||
End = InfinityEnd; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Parses a string to return a <see cref="FontFeature"/>.
|
|||
/// Syntax is the following:
|
|||
///
|
|||
/// Syntax Value Start End
|
|||
/// Setting value:
|
|||
/// kern 1 0 ∞ Turn feature on
|
|||
/// +kern 1 0 ∞ Turn feature on
|
|||
/// -kern 0 0 ∞ Turn feature off
|
|||
/// kern=0 0 0 ∞ Turn feature off
|
|||
/// kern=1 1 0 ∞ Turn feature on
|
|||
/// aalt=2 2 0 ∞ Choose 2nd alternate
|
|||
/// Setting index:
|
|||
/// kern[] 1 0 ∞ Turn feature on
|
|||
/// kern[:] 1 0 ∞ Turn feature on
|
|||
/// kern[5:] 1 5 ∞ Turn feature on, partial
|
|||
/// kern[:5] 1 0 5 Turn feature on, partial
|
|||
/// kern[3:5] 1 3 5 Turn feature on, range
|
|||
/// kern[3] 1 3 3+1 Turn feature on, single char
|
|||
/// Mixing it all:
|
|||
/// aalt[3:5]=2 2 3 5 Turn 2nd alternate on for range
|
|||
///
|
|||
/// </summary>
|
|||
/// <param name="s">The string.</param>
|
|||
/// <returns>The <see cref="FontFeature"/>.</returns>
|
|||
// ReSharper disable once UnusedMember.Global
|
|||
public static FontFeature Parse(string s) |
|||
{ |
|||
var match = s_featureRegex.Match(s); |
|||
|
|||
if (!match.Success) |
|||
{ |
|||
return new FontFeature(); |
|||
} |
|||
|
|||
var hasSeparator = match.Groups["Separator"].Value == ":"; |
|||
var hasStart = int.TryParse(match.Groups["Start"].Value, NumberStyles.None, CultureInfo.InvariantCulture, out var start); |
|||
var hasEnd = int.TryParse(match.Groups["End"].Value, NumberStyles.None, CultureInfo.InvariantCulture, out var end); |
|||
|
|||
var stringValue = match.Groups["Value"].Value; |
|||
if (stringValue == "-" || stringValue.ToUpperInvariant() == "OFF") |
|||
stringValue = "0"; |
|||
if (stringValue == "+" || stringValue.ToUpperInvariant() == "ON") |
|||
stringValue = "1"; |
|||
|
|||
var result = new FontFeature |
|||
{ |
|||
Tag = match.Groups["Tag"].Value, |
|||
Start = hasStart ? start : 0, |
|||
End = hasEnd ? end : hasStart && !hasSeparator ? (start + 1) : InfinityEnd, |
|||
Value = int.TryParse(stringValue, NumberStyles.None, CultureInfo.InvariantCulture, out var value) ? value : DefaultValue, |
|||
}; |
|||
|
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a string representation of the <see cref="FontFeature"/>.
|
|||
/// </summary>
|
|||
/// <returns>The string representation.</returns>
|
|||
public override string ToString() |
|||
{ |
|||
var result = new StringBuilder(128); |
|||
|
|||
if (Value == 0) |
|||
result.Append('-'); |
|||
result.Append(Tag ?? string.Empty); |
|||
|
|||
if (Start != 0 || End != InfinityEnd) |
|||
{ |
|||
result.Append('['); |
|||
|
|||
if (Start > 0) |
|||
result.Append(Start.ToString(CultureInfo.InvariantCulture)); |
|||
|
|||
if (End != Start + 1) |
|||
{ |
|||
result.Append(':'); |
|||
if (End != InfinityEnd) |
|||
result.Append(End.ToString(CultureInfo.InvariantCulture)); |
|||
} |
|||
|
|||
result.Append(']'); |
|||
} |
|||
|
|||
if (Value is DefaultValue or 0) |
|||
{ |
|||
return result.ToString(); |
|||
} |
|||
|
|||
result.Append('='); |
|||
result.Append(Value.ToString(CultureInfo.InvariantCulture)); |
|||
|
|||
return result.ToString(); |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
using Avalonia.Collections; |
|||
|
|||
namespace Avalonia.Media; |
|||
|
|||
/// <summary>
|
|||
/// List of font feature settings
|
|||
/// </summary>
|
|||
public class FontFeatureCollection : AvaloniaList<FontFeature> |
|||
{ |
|||
} |
|||
Loading…
Reference in new issue