Browse Source

Add failing test

pull/10139/head
Benedikt Stebner 4 years ago
parent
commit
3f6cb82241
  1. 101
      src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs
  2. 14
      src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
  3. 8
      tests/Avalonia.Skia.UnitTests/Media/TextFormatting/SingleBufferTextSource.cs
  4. 11
      tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs

101
src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs

@ -60,25 +60,25 @@ namespace Avalonia.Media.TextFormatting
switch (textWrapping) switch (textWrapping)
{ {
case TextWrapping.NoWrap: case TextWrapping.NoWrap:
{ {
// perf note: if textRuns comes from remainingRuns above, it's very likely coming from this class // perf note: if textRuns comes from remainingRuns above, it's very likely coming from this class
// which already uses an array: ToArray() won't ever be called in this case // which already uses an array: ToArray() won't ever be called in this case
var textRunArray = textRuns as TextRun[] ?? textRuns.ToArray(); var textRunArray = textRuns as TextRun[] ?? textRuns.ToArray();
textLine = new TextLineImpl(textRunArray, firstTextSourceIndex, textSourceLength, textLine = new TextLineImpl(textRunArray, firstTextSourceIndex, textSourceLength,
paragraphWidth, paragraphProperties, resolvedFlowDirection, nextLineBreak); paragraphWidth, paragraphProperties, resolvedFlowDirection, nextLineBreak);
textLine.FinalizeLine(); textLine.FinalizeLine();
break; break;
} }
case TextWrapping.WrapWithOverflow: case TextWrapping.WrapWithOverflow:
case TextWrapping.Wrap: case TextWrapping.Wrap:
{ {
textLine = PerformTextWrapping(textRuns, firstTextSourceIndex, paragraphWidth, textLine = PerformTextWrapping(textRuns, firstTextSourceIndex, paragraphWidth,
paragraphProperties, resolvedFlowDirection, nextLineBreak, objectPool, fontManager); paragraphProperties, resolvedFlowDirection, nextLineBreak, objectPool, fontManager);
break; break;
} }
default: default:
throw new ArgumentOutOfRangeException(nameof(textWrapping)); throw new ArgumentOutOfRangeException(nameof(textWrapping));
} }
@ -249,49 +249,49 @@ namespace Avalonia.Media.TextFormatting
switch (currentRun) switch (currentRun)
{ {
case UnshapedTextRun shapeableRun: case UnshapedTextRun shapeableRun:
{ {
groupedRuns.Clear(); groupedRuns.Clear();
groupedRuns.Add(shapeableRun); groupedRuns.Add(shapeableRun);
var text = shapeableRun.Text; var text = shapeableRun.Text;
var properties = shapeableRun.Properties; var properties = shapeableRun.Properties;
while (index + 1 < processedRuns.Count) while (index + 1 < processedRuns.Count)
{
if (processedRuns[index + 1] is not UnshapedTextRun nextRun)
{ {
if (processedRuns[index + 1] is not UnshapedTextRun nextRun)
{
break;
}
if (shapeableRun.BidiLevel == nextRun.BidiLevel
&& TryJoinContiguousMemories(text, nextRun.Text, out var joinedText)
&& CanShapeTogether(properties, nextRun.Properties))
{
groupedRuns.Add(nextRun);
index++;
shapeableRun = nextRun;
text = joinedText;
continue;
}
break; break;
} }
if (shapeableRun.BidiLevel == nextRun.BidiLevel var shaperOptions = new TextShaperOptions(
&& TryJoinContiguousMemories(text, nextRun.Text, out var joinedText) properties.CachedGlyphTypeface,
&& CanShapeTogether(properties, nextRun.Properties)) properties.FontRenderingEmSize, shapeableRun.BidiLevel, properties.CultureInfo,
{ paragraphProperties.DefaultIncrementalTab, paragraphProperties.LetterSpacing);
groupedRuns.Add(nextRun);
index++; ShapeTogether(groupedRuns, text, shaperOptions, textShaper, shapedRuns);
shapeableRun = nextRun;
text = joinedText;
continue;
}
break; break;
} }
var shaperOptions = new TextShaperOptions(
properties.CachedGlyphTypeface,
properties.FontRenderingEmSize, shapeableRun.BidiLevel, properties.CultureInfo,
paragraphProperties.DefaultIncrementalTab, paragraphProperties.LetterSpacing);
ShapeTogether(groupedRuns, text, shaperOptions, textShaper, shapedRuns);
break;
}
default: default:
{ {
shapedRuns.Add(currentRun); shapedRuns.Add(currentRun);
break; break;
} }
} }
} }
} }
@ -712,7 +712,7 @@ namespace Avalonia.Media.TextFormatting
switch (currentRun) switch (currentRun)
{ {
case ShapedTextRun: case ShapedTextRun:
{ {
var lineBreaker = new LineBreakEnumerator(currentRun.Text.Span); var lineBreaker = new LineBreakEnumerator(currentRun.Text.Span);
while (lineBreaker.MoveNext(out var lineBreak)) while (lineBreaker.MoveNext(out var lineBreak))
@ -754,7 +754,7 @@ namespace Avalonia.Media.TextFormatting
break; break;
} }
while (lineBreaker.MoveNext(out lineBreak) && index < textRuns.Count) while (lineBreaker.MoveNext(out lineBreak))
{ {
currentPosition += lineBreak.PositionWrap; currentPosition += lineBreak.PositionWrap;
@ -780,6 +780,11 @@ namespace Avalonia.Media.TextFormatting
currentPosition = currentLength + lineBreak.PositionWrap; currentPosition = currentLength + lineBreak.PositionWrap;
} }
//if (currentPosition == 0 && measuredLength > 0)
//{
// currentPosition = measuredLength;
//}
breakFound = true; breakFound = true;
break; break;

14
src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs

@ -172,9 +172,21 @@ namespace Avalonia.Media.TextFormatting
distance -= Start; distance -= Start;
var firstRunIndex = 0;
if (_textRuns[firstRunIndex] is TextEndOfLine)
{
firstRunIndex++;
}
if(firstRunIndex >= _textRuns.Length)
{
return new CharacterHit(FirstTextSourceIndex);
}
if (distance <= 0) if (distance <= 0)
{ {
var firstRun = _textRuns[0]; var firstRun = _textRuns[firstRunIndex];
return GetRunCharacterHit(firstRun, FirstTextSourceIndex, 0); return GetRunCharacterHit(firstRun, FirstTextSourceIndex, 0);
} }

8
tests/Avalonia.Skia.UnitTests/Media/TextFormatting/SingleBufferTextSource.cs

@ -7,25 +7,27 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
{ {
private readonly string _text; private readonly string _text;
private readonly GenericTextRunProperties _defaultGenericPropertiesRunProperties; private readonly GenericTextRunProperties _defaultGenericPropertiesRunProperties;
private readonly bool _addEndOfParagraph;
public SingleBufferTextSource(string text, GenericTextRunProperties defaultProperties) public SingleBufferTextSource(string text, GenericTextRunProperties defaultProperties, bool addEndOfParagraph = false)
{ {
_text = text; _text = text;
_defaultGenericPropertiesRunProperties = defaultProperties; _defaultGenericPropertiesRunProperties = defaultProperties;
_addEndOfParagraph = addEndOfParagraph;
} }
public TextRun GetTextRun(int textSourceIndex) public TextRun GetTextRun(int textSourceIndex)
{ {
if (textSourceIndex >= _text.Length) if (textSourceIndex >= _text.Length)
{ {
return null; return _addEndOfParagraph ? new TextEndOfParagraph() : null;
} }
var runText = _text.AsMemory(textSourceIndex); var runText = _text.AsMemory(textSourceIndex);
if (runText.IsEmpty) if (runText.IsEmpty)
{ {
return null; return _addEndOfParagraph ? new TextEndOfParagraph() : null;
} }
return new TextCharacters(runText, _defaultGenericPropertiesRunProperties); return new TextCharacters(runText, _defaultGenericPropertiesRunProperties);

11
tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs

@ -242,10 +242,16 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
{ {
var defaultProperties = new GenericTextRunProperties(Typeface.Default); var defaultProperties = new GenericTextRunProperties(Typeface.Default);
var textSource = new SingleBufferTextSource(text, defaultProperties); var paragraphProperties = new GenericTextParagraphProperties(defaultProperties, textWrap: TextWrapping.WrapWithOverflow);
var textSource = new SingleBufferTextSource("ABCDEFHFFHFJHKHFK", defaultProperties, true);
var formatter = new TextFormatterImpl(); var formatter = new TextFormatterImpl();
var line = formatter.FormatLine(textSource, 0, 33, paragraphProperties);
textSource = new SingleBufferTextSource(text, defaultProperties);
var numberOfLines = 0; var numberOfLines = 0;
var currentPosition = 0; var currentPosition = 0;
@ -253,8 +259,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
while (currentPosition < text.Length) while (currentPosition < text.Length)
{ {
var textLine = var textLine =
formatter.FormatLine(textSource, currentPosition, 1, formatter.FormatLine(textSource, currentPosition, 1, paragraphProperties);
new GenericTextParagraphProperties(defaultProperties, textWrap: TextWrapping.WrapWithOverflow));
if (text.Length - currentPosition > expectedCharactersPerLine) if (text.Length - currentPosition > expectedCharactersPerLine)
{ {

Loading…
Cancel
Save