@ -0,0 +1,32 @@ |
|||
namespace Avalonia.Media |
|||
{ |
|||
/// <summary>
|
|||
/// Enum specifying where a box should be positioned Vertically
|
|||
/// </summary>
|
|||
public enum BaselineAlignment |
|||
{ |
|||
/// <summary>Align top toward top of container</summary>
|
|||
Top, |
|||
|
|||
/// <summary>Center vertically</summary>
|
|||
Center, |
|||
|
|||
/// <summary>Align bottom toward bottom of container</summary>
|
|||
Bottom, |
|||
|
|||
/// <summary>Align at baseline</summary>
|
|||
Baseline, |
|||
|
|||
/// <summary>Align toward text's top of container</summary>
|
|||
TextTop, |
|||
|
|||
/// <summary>Align toward text's bottom of container</summary>
|
|||
TextBottom, |
|||
|
|||
/// <summary>Align baseline to subscript position of container</summary>
|
|||
Subscript, |
|||
|
|||
/// <summary>Align baseline to superscript position of container</summary>
|
|||
Superscript, |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
namespace Avalonia.Media |
|||
{ |
|||
/// <summary>
|
|||
/// The 'flow-direction' property specifies whether the primary text advance
|
|||
/// direction shall be left-to-right or right-to-left.
|
|||
/// </summary>
|
|||
public enum FlowDirection |
|||
{ |
|||
/// <internalonly>
|
|||
/// Sets the primary text advance direction to left-to-right, and the line
|
|||
/// progression direction to top-to-bottom as is common in most Roman-based
|
|||
/// documents. For most characters, the current text position is advanced
|
|||
/// from left to right after each glyph is rendered. The 'direction' property
|
|||
/// is set to 'ltr'.
|
|||
/// </internalonly>
|
|||
LeftToRight, |
|||
|
|||
/// <internalonly>
|
|||
/// Sets the primary text advance direction to right-to-left, and the line
|
|||
/// progression direction to top-to-bottom as is common in Arabic or Hebrew
|
|||
/// scripts. The direction property is set to 'rtl'.
|
|||
/// </internalonly>
|
|||
RightToLeft |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
namespace Avalonia.Media |
|||
{ |
|||
public readonly struct GlyphRunMetrics |
|||
{ |
|||
public GlyphRunMetrics(double width, double widthIncludingTrailingWhitespace, int trailingWhitespaceLength, |
|||
int newlineLength, double height) |
|||
{ |
|||
Width = width; |
|||
WidthIncludingTrailingWhitespace = widthIncludingTrailingWhitespace; |
|||
TrailingWhitespaceLength = trailingWhitespaceLength; |
|||
NewlineLength = newlineLength; |
|||
Height = height; |
|||
} |
|||
|
|||
public double Width { get; } |
|||
|
|||
public double WidthIncludingTrailingWhitespace { get; } |
|||
|
|||
public int TrailingWhitespaceLength { get; } |
|||
|
|||
public int NewlineLength { get; } |
|||
|
|||
public double Height { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
namespace Avalonia.Media.TextFormatting |
|||
{ |
|||
public enum LogicalDirection |
|||
{ |
|||
/// <summary>
|
|||
/// Backward, or from right to left.
|
|||
/// </summary>
|
|||
Backward, |
|||
/// <summary>
|
|||
/// Forward, or from left to right.
|
|||
/// </summary>
|
|||
Forward |
|||
} |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
using System; |
|||
using System.Globalization; |
|||
using Avalonia.Media; |
|||
using Avalonia.Media.TextFormatting; |
|||
using Avalonia.UnitTests; |
|||
using Xunit; |
|||
|
|||
namespace Avalonia.Skia.UnitTests.Media |
|||
{ |
|||
public class GlyphRunTests |
|||
{ |
|||
[InlineData("ABC \r", 29, 4, 1)] |
|||
[InlineData("ABC \r", 23, 3, 1)] |
|||
[InlineData("ABC \r", 17, 2, 1)] |
|||
[InlineData("ABC \r", 11, 1, 1)] |
|||
[InlineData("ABC \r", 7, 1, 0)] |
|||
[InlineData("ABC \r", 5, 0, 1)] |
|||
[InlineData("ABC \r", 2, 0, 0)] |
|||
[Theory] |
|||
public void Should_Get_Distance_From_CharacterHit(string text, double distance, int expectedIndex, |
|||
int expectedTrailingLength) |
|||
{ |
|||
using (Start()) |
|||
{ |
|||
var glyphRun = |
|||
TextShaper.Current.ShapeText(text.AsMemory(), Typeface.Default, 10, CultureInfo.CurrentCulture); |
|||
|
|||
var characterHit = glyphRun.GetCharacterHitFromDistance(distance, out _); |
|||
|
|||
Assert.Equal(expectedIndex, characterHit.FirstCharacterIndex); |
|||
|
|||
Assert.Equal(expectedTrailingLength, characterHit.TrailingLength); |
|||
} |
|||
} |
|||
|
|||
private static IDisposable Start() |
|||
{ |
|||
var disposable = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface |
|||
.With(renderInterface: new PlatformRenderInterface(null), |
|||
textShaperImpl: new TextShaperImpl(), |
|||
fontManagerImpl: new CustomFontManagerImpl())); |
|||
|
|||
return disposable; |
|||
} |
|||
} |
|||
} |
|||
@ -1,38 +0,0 @@ |
|||
using System; |
|||
using Avalonia.Media.TextFormatting; |
|||
using Avalonia.Utilities; |
|||
|
|||
namespace Avalonia.Skia.UnitTests.Media.TextFormatting |
|||
{ |
|||
internal class FormattableTextSource : ITextSource |
|||
{ |
|||
private readonly ReadOnlySlice<char> _text; |
|||
private readonly TextRunProperties _defaultStyle; |
|||
private ReadOnlySlice<ValueSpan<TextRunProperties>> _styleSpans; |
|||
|
|||
public FormattableTextSource(string text, TextRunProperties defaultStyle, |
|||
ReadOnlySlice<ValueSpan<TextRunProperties>> styleSpans) |
|||
{ |
|||
_text = text.AsMemory(); |
|||
|
|||
_defaultStyle = defaultStyle; |
|||
|
|||
_styleSpans = styleSpans; |
|||
} |
|||
|
|||
public TextRun GetTextRun(int textSourceIndex) |
|||
{ |
|||
if (_styleSpans.IsEmpty) |
|||
{ |
|||
return new TextEndOfParagraph(); |
|||
} |
|||
|
|||
var currentSpan = _styleSpans[0]; |
|||
|
|||
_styleSpans = _styleSpans.Skip(1); |
|||
|
|||
return new TextCharacters(_text.AsSlice(currentSpan.Start, currentSpan.Length), |
|||
_defaultStyle); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,121 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Avalonia.Media.TextFormatting; |
|||
using Avalonia.Utilities; |
|||
|
|||
namespace Avalonia.Skia.UnitTests.Media.TextFormatting |
|||
{ |
|||
internal readonly struct FormattedTextSource : ITextSource |
|||
{ |
|||
private readonly ReadOnlySlice<char> _text; |
|||
private readonly TextRunProperties _defaultProperties; |
|||
private readonly IReadOnlyList<ValueSpan<TextRunProperties>> _textModifier; |
|||
|
|||
public FormattedTextSource(ReadOnlySlice<char> text, TextRunProperties defaultProperties, |
|||
IReadOnlyList<ValueSpan<TextRunProperties>> textModifier) |
|||
{ |
|||
_text = text; |
|||
_defaultProperties = defaultProperties; |
|||
_textModifier = textModifier; |
|||
} |
|||
|
|||
public TextRun GetTextRun(int textSourceIndex) |
|||
{ |
|||
if (textSourceIndex > _text.End) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
var runText = _text.Skip(textSourceIndex); |
|||
|
|||
if (runText.IsEmpty) |
|||
{ |
|||
return new TextEndOfParagraph(); |
|||
} |
|||
|
|||
var textStyleRun = CreateTextStyleRun(runText, _defaultProperties, _textModifier); |
|||
|
|||
return new TextCharacters(runText.Take(textStyleRun.Length), textStyleRun.Value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates a span of text run properties that has modifier applied.
|
|||
/// </summary>
|
|||
/// <param name="text">The text to create the properties for.</param>
|
|||
/// <param name="defaultProperties">The default text properties.</param>
|
|||
/// <param name="textModifier">The text properties modifier.</param>
|
|||
/// <returns>
|
|||
/// The created text style run.
|
|||
/// </returns>
|
|||
private static ValueSpan<TextRunProperties> CreateTextStyleRun(ReadOnlySlice<char> text, |
|||
TextRunProperties defaultProperties, IReadOnlyList<ValueSpan<TextRunProperties>> textModifier) |
|||
{ |
|||
if (textModifier == null || textModifier.Count == 0) |
|||
{ |
|||
return new ValueSpan<TextRunProperties>(text.Start, text.Length, defaultProperties); |
|||
} |
|||
|
|||
var currentProperties = defaultProperties; |
|||
|
|||
var hasOverride = false; |
|||
|
|||
var i = 0; |
|||
|
|||
var length = 0; |
|||
|
|||
for (; i < textModifier.Count; i++) |
|||
{ |
|||
var propertiesOverride = textModifier[i]; |
|||
|
|||
var textRange = new TextRange(propertiesOverride.Start, propertiesOverride.Length); |
|||
|
|||
if (textRange.End < text.Start) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
if (textRange.Start > text.End) |
|||
{ |
|||
length = text.Length; |
|||
break; |
|||
} |
|||
|
|||
if (textRange.Start > text.Start) |
|||
{ |
|||
if (propertiesOverride.Value != currentProperties) |
|||
{ |
|||
length = Math.Min(Math.Abs(textRange.Start - text.Start), text.Length); |
|||
|
|||
break; |
|||
} |
|||
} |
|||
|
|||
length += Math.Min(text.Length - length, textRange.Length); |
|||
|
|||
if (hasOverride) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
hasOverride = true; |
|||
|
|||
currentProperties = propertiesOverride.Value; |
|||
} |
|||
|
|||
if (length < text.Length && i == textModifier.Count) |
|||
{ |
|||
if (currentProperties == defaultProperties) |
|||
{ |
|||
length = text.Length; |
|||
} |
|||
} |
|||
|
|||
if (length != text.Length) |
|||
{ |
|||
text = text.Take(length); |
|||
} |
|||
|
|||
return new ValueSpan<TextRunProperties>(text.Start, length, currentProperties); |
|||
} |
|||
} |
|||
} |
|||
|
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 404 B After Width: | Height: | Size: 403 B |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 370 B After Width: | Height: | Size: 370 B |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |