Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,12 @@ internal static class TextEllipsisHelper
{
var currentBreakPosition = 0;

var lineBreaker = new LineBreakEnumerator(currentRun.Text.Span);
var text = currentRun.Text.Span;
var wordBreaker = new WordBreakEnumerator(text);

while (currentBreakPosition < measuredLength && lineBreaker.MoveNext(out var lineBreak))
while (currentBreakPosition < measuredLength && wordBreaker.MoveNext(out var wordSegment))
{
var nextBreakPosition = lineBreak.PositionMeasure;
var nextBreakPosition = wordSegment.Offset + wordSegment.Length;

if (nextBreakPosition == 0)
{
Expand All @@ -57,6 +58,17 @@ internal static class TextEllipsisHelper
break;
}

var firstCodepoint = Codepoint.ReadAt(text, wordSegment.Offset, out _);

if (firstCodepoint.WordBreakClass == WordBreakClass.WSegSpace)
{
// UAX #29 allows boundaries on both sides of WSegSpace; use the start
// to preserve the existing behavior of trimming spaces before the ellipsis.
currentBreakPosition = wordSegment.Offset;

continue;
}

currentBreakPosition = nextBreakPosition;
}

Expand Down
561 changes: 288 additions & 273 deletions src/Avalonia.Base/Media/TextFormatting/Unicode/BiDi.trie.cs

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ public static Codepoint ReplacementCodepoint
/// </summary>
public LineBreakClass LineBreakClass => UnicodeData.GetLineBreakClass(_value);

/// <summary>
/// Gets the <see cref="Unicode.WordBreakClass"/>.
/// </summary>
public WordBreakClass WordBreakClass => UnicodeData.GetWordBreakClass(_value);

/// <summary>
/// Gets the <see cref="GraphemeBreakClass"/>.
/// </summary>
Expand Down
934 changes: 474 additions & 460 deletions src/Avalonia.Base/Media/TextFormatting/Unicode/EastAsianWidth.trie.cs

Large diffs are not rendered by default.

1,449 changes: 797 additions & 652 deletions src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeBreak.trie.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public ref struct GraphemeEnumerator
private int _currentCodeUnitOffset;
private int _codeUnitLengthOfCurrentCodepoint;
private Codepoint _currentCodepoint;
private IndicConjunctBreakClass _currentIndicConjunctBreakType;

/// <summary>
/// Will be <see cref="GraphemeBreakClass.Other"/> if invalid data or EOF reached.
Expand All @@ -26,6 +27,7 @@ public GraphemeEnumerator(ReadOnlySpan<char> text)
_currentCodeUnitOffset = 0;
_codeUnitLengthOfCurrentCodepoint = 0;
_currentCodepoint = Codepoint.ReplacementCodepoint;
_currentIndicConjunctBreakType = IndicConjunctBreakClass.None;
_currentType = GraphemeBreakClass.Other;
}

Expand Down Expand Up @@ -84,6 +86,40 @@ public bool MoveNext(out Grapheme grapheme)

// Now begin the main state machine.

var hasIndicConjunctLinker = false;
var hasIndicConjunctBase = false;

// GB9c is stateful: only codepoints already consumed into this cluster
// can make a following InCB=Consonant join instead of starting a new cluster.
void ConsumeIndicConjunctBreak(IndicConjunctBreakClass indicConjunctBreakType)
{
switch (indicConjunctBreakType)
{
case IndicConjunctBreakClass.Consonant:
hasIndicConjunctBase = true;
hasIndicConjunctLinker = false;
break;

case IndicConjunctBreakClass.Linker:
if (hasIndicConjunctBase)
{
hasIndicConjunctLinker = true;
}

break;

case IndicConjunctBreakClass.Extend:
break;

default:
hasIndicConjunctBase = false;
hasIndicConjunctLinker = false;
break;
}
}

ConsumeIndicConjunctBreak(_currentIndicConjunctBreakType);

var previousClusterBreakType = _currentType;

ReadNextCodepoint();
Expand Down Expand Up @@ -204,11 +240,30 @@ public bool MoveNext(out Grapheme grapheme)
(1U << (int)GraphemeBreakClass.SpacingMark);

// rules GB9, GB9a
// Keep trailers with the current cluster, and feed them into the GB9c
// state so InCB=Extend stays transparent between consonants and linkers.
while (((1U << (int)_currentType) & gb9Mask) != 0U)
{
ConsumeIndicConjunctBreak(_currentIndicConjunctBreakType);
ReadNextCodepoint();
}

// GB9c keeps Indic conjunct clusters together once a consonant has been
// followed by a linker, with any GB9 extenders allowed on both sides.
while (hasIndicConjunctBase &&
hasIndicConjunctLinker &&
_currentIndicConjunctBreakType == IndicConjunctBreakClass.Consonant)
{
ConsumeIndicConjunctBreak(_currentIndicConjunctBreakType);
ReadNextCodepoint();

while (((1U << (int)_currentType) & gb9Mask) != 0U)
{
ConsumeIndicConjunctBreak(_currentIndicConjunctBreakType);
ReadNextCodepoint();
}
}

Return:

var graphemeLength = _currentCodeUnitOffset - startOffset;
Expand Down Expand Up @@ -238,6 +293,7 @@ private void ReadNextCodepoint()
out _codeUnitLengthOfCurrentCodepoint);

_currentType = _currentCodepoint.GraphemeBreakClass;
_currentIndicConjunctBreakType = UnicodeData.GetIndicConjunctBreakClass(_currentCodepoint.Value);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Avalonia.Media.TextFormatting.Unicode
{
internal enum IndicConjunctBreakClass
{
None,
Linker,
Consonant,
Extend
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public enum LineBreakClass
MandatoryBreak, //BK
ConditionalJapaneseStarter, //CJ
CarriageReturn, //CR
UnambiguousHyphen, //HH
LineFeed, //LF
NextLine, //NL
ComplexContext, //SA
Expand Down
Loading