diff --git a/src/Avalonia.Base/Media/FormattedText.cs b/src/Avalonia.Base/Media/FormattedText.cs index 3b63a98720..d4640390d7 100644 --- a/src/Avalonia.Base/Media/FormattedText.cs +++ b/src/Avalonia.Base/Media/FormattedText.cs @@ -877,7 +877,7 @@ namespace Avalonia.Media var lastRunProps = (GenericTextRunProperties)thatFormatRider.CurrentElement!; - TextCollapsingProperties collapsingProperties = _that._trimming.CreateCollapsingProperties(new TextCollapsingCreateInfo(maxLineLength, lastRunProps)); + TextCollapsingProperties collapsingProperties = _that._trimming.CreateCollapsingProperties(new TextCollapsingCreateInfo(maxLineLength, lastRunProps, paraProps.FlowDirection)); var collapsedLine = line.Collapse(collapsingProperties); diff --git a/src/Avalonia.Base/Media/TextCollapsingCreateInfo.cs b/src/Avalonia.Base/Media/TextCollapsingCreateInfo.cs index 40ba613717..9b7bf3f74c 100644 --- a/src/Avalonia.Base/Media/TextCollapsingCreateInfo.cs +++ b/src/Avalonia.Base/Media/TextCollapsingCreateInfo.cs @@ -6,11 +6,13 @@ namespace Avalonia.Media { public readonly double Width; public readonly TextRunProperties TextRunProperties; + public readonly FlowDirection FlowDirection; - public TextCollapsingCreateInfo(double width, TextRunProperties textRunProperties) + public TextCollapsingCreateInfo(double width, TextRunProperties textRunProperties, FlowDirection flowDirection) { Width = width; TextRunProperties = textRunProperties; + FlowDirection = flowDirection; } } } diff --git a/src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs b/src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs index 0d85f3e7c5..c1b9b77401 100644 --- a/src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs +++ b/src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs @@ -27,16 +27,6 @@ namespace Avalonia.Media.TextFormatting return; } - if (lineImpl.NewLineLength > 0) - { - return; - } - - if (lineImpl.TextLineBreak is { TextEndOfLine: not null, IsSplit: false }) - { - return; - } - var breakOportunities = new Queue(); var currentPosition = textLine.FirstTextSourceIndex; @@ -97,7 +87,8 @@ namespace Avalonia.Media.TextFormatting continue; } - var glyphIndex = glyphRun.FindGlyphIndex(characterIndex); + var offset = Math.Max(0, currentPosition - glyphRun.Metrics.FirstCluster); + var glyphIndex = glyphRun.FindGlyphIndex(characterIndex - offset); var glyphInfo = shapedBuffer.GlyphInfos[glyphIndex]; shapedBuffer.GlyphInfos[glyphIndex] = new GlyphInfo(glyphInfo.GlyphIndex, diff --git a/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs b/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs index 7f23ac98b4..568148e15c 100644 --- a/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs +++ b/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs @@ -148,33 +148,38 @@ namespace Avalonia.Media.TextFormatting internal SplitResult Split(int length) { - if (IsReversed) + var isReversed = IsReversed; + + if (isReversed) { Reverse(); - } + length = Length - length; + } #if DEBUG - if(length == 0) + if (length == 0) { throw new ArgumentOutOfRangeException(nameof(length), "length must be greater than zero."); } -#endif - +#endif var splitBuffer = ShapedBuffer.Split(length); var first = new ShapedTextRun(splitBuffer.First, Properties); - #if DEBUG +#if DEBUG if (first.Length != length) { throw new InvalidOperationException("Split length mismatch."); } - #endif - var second = new ShapedTextRun(splitBuffer.Second!, Properties); + if (isReversed) + { + return new SplitResult(second, first); + } + return new SplitResult(first, second); } diff --git a/src/Avalonia.Base/Media/TextFormatting/TextCollapsingProperties.cs b/src/Avalonia.Base/Media/TextFormatting/TextCollapsingProperties.cs index 72882df0b5..7cdf81ecc9 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextCollapsingProperties.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextCollapsingProperties.cs @@ -1,4 +1,6 @@ -namespace Avalonia.Media.TextFormatting +using System.Collections.Generic; + +namespace Avalonia.Media.TextFormatting { /// /// Properties of text collapsing. @@ -15,6 +17,11 @@ /// public abstract TextRun Symbol { get; } + /// + /// Gets the flow direction that is used for collapsing. + /// + public abstract FlowDirection FlowDirection { get; } + /// /// Collapses given text line. /// diff --git a/src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs b/src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs index 4c93a1d851..6422f23dcd 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using Avalonia.Media.TextFormatting.Unicode; namespace Avalonia.Media.TextFormatting @@ -28,97 +27,191 @@ namespace Avalonia.Media.TextFormatting var availableWidth = properties.Width - shapedSymbol.Size.Width; - while (runIndex < textRuns.Count) + if(properties.FlowDirection== FlowDirection.LeftToRight) { - var currentRun = textRuns[runIndex]; - - switch (currentRun) + while (runIndex < textRuns.Count) { - case ShapedTextRun shapedRun: - { - currentWidth += shapedRun.Size.Width; + var currentRun = textRuns[runIndex]; - if (currentWidth > availableWidth) + switch (currentRun) + { + case ShapedTextRun shapedRun: { - if (shapedRun.TryMeasureCharacters(availableWidth, out var measuredLength)) + currentWidth += shapedRun.Size.Width; + + if (currentWidth > availableWidth) { - if (isWordEllipsis && measuredLength < textLine.Length) + if (shapedRun.TryMeasureCharacters(availableWidth, out var measuredLength)) { - var currentBreakPosition = 0; - - var lineBreaker = new LineBreakEnumerator(currentRun.Text.Span); - - while (currentBreakPosition < measuredLength && lineBreaker.MoveNext(out var lineBreak)) + if (isWordEllipsis && measuredLength < textLine.Length) { - var nextBreakPosition = lineBreak.PositionMeasure; + var currentBreakPosition = 0; - if (nextBreakPosition == 0) - { - break; - } + var lineBreaker = new LineBreakEnumerator(currentRun.Text.Span); - if (nextBreakPosition >= measuredLength) + while (currentBreakPosition < measuredLength && lineBreaker.MoveNext(out var lineBreak)) { - break; + var nextBreakPosition = lineBreak.PositionMeasure; + + if (nextBreakPosition == 0) + { + break; + } + + if (nextBreakPosition >= measuredLength) + { + break; + } + + currentBreakPosition = nextBreakPosition; } - currentBreakPosition = nextBreakPosition; + measuredLength = currentBreakPosition; } - - measuredLength = currentBreakPosition; } + + collapsedLength += measuredLength; + + return CreateCollapsedRuns(textLine, collapsedLength, FlowDirection.LeftToRight, shapedSymbol); } - collapsedLength += measuredLength; + availableWidth -= shapedRun.Size.Width; - return CreateCollapsedRuns(textRuns, collapsedLength, shapedSymbol); + break; } - availableWidth -= shapedRun.Size.Width; + case DrawableTextRun drawableRun: + { + //The whole run needs to fit into available space + if (currentWidth + drawableRun.Size.Width > availableWidth) + { + return CreateCollapsedRuns(textLine, collapsedLength, FlowDirection.LeftToRight, shapedSymbol); + } - break; - } + availableWidth -= drawableRun.Size.Width; - case DrawableTextRun drawableRun: - { - //The whole run needs to fit into available space - if (currentWidth + drawableRun.Size.Width > availableWidth) - { - return CreateCollapsedRuns(textRuns, collapsedLength, shapedSymbol); + break; } + } - availableWidth -= drawableRun.Size.Width; + collapsedLength += currentRun.Length; - break; - } + runIndex++; } + } + else + { + runIndex = textRuns.Count - 1; + + while (runIndex >= 0) + { + var currentRun = textRuns[runIndex]; - collapsedLength += currentRun.Length; + switch (currentRun) + { + case ShapedTextRun shapedRun: + { + currentWidth += shapedRun.Size.Width; - runIndex++; - } + if (currentWidth > availableWidth) + { + if (shapedRun.TryMeasureCharacters(availableWidth, out var measuredLength)) + { + if (isWordEllipsis && measuredLength < textLine.Length) + { + var currentBreakPosition = 0; + + var lineBreaker = new LineBreakEnumerator(currentRun.Text.Span); + + while (currentBreakPosition < measuredLength && lineBreaker.MoveNext(out var lineBreak)) + { + var nextBreakPosition = lineBreak.PositionMeasure; + + if (nextBreakPosition == 0) + { + break; + } + + if (nextBreakPosition >= measuredLength) + { + break; + } + currentBreakPosition = nextBreakPosition; + } + + measuredLength = currentBreakPosition; + } + } + + collapsedLength += measuredLength; + + return CreateCollapsedRuns(textLine, collapsedLength, FlowDirection.RightToLeft, shapedSymbol); + } + + availableWidth -= shapedRun.Size.Width; + + break; + } + + case DrawableTextRun drawableRun: + { + //The whole run needs to fit into available space + if (currentWidth + drawableRun.Size.Width > availableWidth) + { + return CreateCollapsedRuns(textLine, collapsedLength, FlowDirection.RightToLeft, shapedSymbol); + } + + availableWidth -= drawableRun.Size.Width; + + break; + } + } + + collapsedLength += currentRun.Length; + + runIndex--; + } + } + return null; } - private static TextRun[] CreateCollapsedRuns(IReadOnlyList textRuns, int collapsedLength, - TextRun shapedSymbol) + private static TextRun[] CreateCollapsedRuns(TextLine textLine, int collapsedLength, + FlowDirection flowDirection, TextRun shapedSymbol) { + var textRuns = textLine.TextRuns; + if (collapsedLength <= 0) { return new[] { shapedSymbol }; } + if(flowDirection == FlowDirection.RightToLeft) + { + collapsedLength = textLine.Length - collapsedLength; + } + var objectPool = FormattingObjectPool.Instance; var (preSplitRuns, postSplitRuns) = TextFormatterImpl.SplitTextRuns(textRuns, collapsedLength, objectPool); try { - var collapsedRuns = new TextRun[preSplitRuns.Count + 1]; - preSplitRuns.CopyTo(collapsedRuns); - collapsedRuns[collapsedRuns.Length - 1] = shapedSymbol; - return collapsedRuns; + if (flowDirection == FlowDirection.RightToLeft) + { + var collapsedRuns = new TextRun[postSplitRuns!.Count + 1]; + postSplitRuns.CopyTo(collapsedRuns, 1); + collapsedRuns[0] = shapedSymbol; + return collapsedRuns; + } + else + { + var collapsedRuns = new TextRun[preSplitRuns!.Count + 1]; + preSplitRuns.CopyTo(collapsedRuns); + collapsedRuns[collapsedRuns.Length - 1] = shapedSymbol; + return collapsedRuns; + } } finally { diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs b/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs index 4dbc472133..a382416b8a 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs @@ -352,7 +352,7 @@ namespace Avalonia.Media.TextFormatting var lastTrailingIndex = 0; - if(_paragraphProperties.FlowDirection== FlowDirection.LeftToRight) + if (_paragraphProperties.FlowDirection == FlowDirection.LeftToRight) { lastTrailingIndex = textLine.FirstTextSourceIndex + textLine.Length; @@ -377,7 +377,7 @@ namespace Avalonia.Media.TextFormatting { lastTrailingIndex += textEndOfLine.Length; } - } + } var textPosition = characterHit.FirstCharacterIndex + characterHit.TrailingLength; @@ -553,26 +553,18 @@ namespace Avalonia.Media.TextFormatting if (_paragraphProperties.TextAlignment == TextAlignment.Justify) { - var whitespaceWidth = 0d; + var justificationWidth = MaxWidth; - for (var i = 0; i < textLines.Count; i++) + if (_paragraphProperties.TextWrapping != TextWrapping.NoWrap) { - var line = textLines[i]; - var lineWhitespaceWidth = line.Width - line.WidthIncludingTrailingWhitespace; - - if (lineWhitespaceWidth > whitespaceWidth) - { - whitespaceWidth = lineWhitespaceWidth; - } + justificationWidth = width; } - var justificationWidth = width - whitespaceWidth; - if (justificationWidth > 0) { var justificationProperties = new InterWordJustification(justificationWidth); - for (var i = 0; i < textLines.Count - 1; i++) + for (var i = 0; i < textLines.Count; i++) { var line = textLines[i]; @@ -597,12 +589,13 @@ namespace Avalonia.Media.TextFormatting /// The . private TextCollapsingProperties? GetCollapsingProperties(double width) { - if(_textTrimming == TextTrimming.None) + if (_textTrimming == TextTrimming.None) { return null; } - return _textTrimming.CreateCollapsingProperties(new TextCollapsingCreateInfo(width, _paragraphProperties.DefaultTextRunProperties)); + return _textTrimming.CreateCollapsingProperties( + new TextCollapsingCreateInfo(width, _paragraphProperties.DefaultTextRunProperties, _paragraphProperties.FlowDirection)); } public void Dispose() diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs b/src/Avalonia.Base/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs index 2e85b1e187..a21a5d45e9 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs @@ -19,11 +19,13 @@ namespace Avalonia.Media.TextFormatting /// Length of leading prefix. /// width in which collapsing is constrained to /// text run properties of ellipsis symbol + /// the flow direction of the collapes line. public TextLeadingPrefixCharacterEllipsis( string ellipsis, int prefixLength, double width, - TextRunProperties textRunProperties) + TextRunProperties textRunProperties, + FlowDirection flowDirection) { if (_prefixLength < 0) { @@ -33,6 +35,7 @@ namespace Avalonia.Media.TextFormatting _prefixLength = prefixLength; Width = width; Symbol = new TextCharacters(ellipsis, textRunProperties); + FlowDirection = flowDirection; } /// @@ -41,6 +44,8 @@ namespace Avalonia.Media.TextFormatting /// public override TextRun Symbol { get; } + public override FlowDirection FlowDirection { get; } + /// public override TextRun[]? Collapse(TextLine textLine) { diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs index 187b3154ad..b3321d4d9f 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs @@ -658,7 +658,7 @@ namespace Avalonia.Media.TextFormatting currentX += drawableTextRun.Size.Width; } - if(lastRunIndex - 1 < 0) + if (lastRunIndex - 1 < 0) { break; } @@ -685,7 +685,7 @@ namespace Avalonia.Media.TextFormatting directionalWidth -= drawableTextRun.Size.Width; } - if(firstRunIndex + 1 == _textRuns.Length) + if (firstRunIndex + 1 == _textRuns.Length) { break; } @@ -1097,7 +1097,7 @@ namespace Avalonia.Media.TextFormatting var runWidth = endX - startX; - return new TextRunBounds(new Rect(Start + startX, 0, runWidth, Height), currentPosition, characterLength, currentRun); + return new TextRunBounds(new Rect(startX, 0, runWidth, Height), currentPosition, characterLength, currentRun); } public override void Dispose() @@ -1439,13 +1439,6 @@ namespace Avalonia.Media.TextFormatting } } - if (index == lastRunIndex) - { - width = widthIncludingWhitespace + textRun.GlyphRun.Metrics.Width; - trailingWhitespaceLength = textRun.GlyphRun.Metrics.TrailingWhitespaceLength; - newLineLength += textRun.GlyphRun.Metrics.NewLineLength; - } - widthIncludingWhitespace += textRun.Size.Width; break; @@ -1455,12 +1448,6 @@ namespace Avalonia.Media.TextFormatting { widthIncludingWhitespace += drawableTextRun.Size.Width; - if (index == lastRunIndex) - { - width = widthIncludingWhitespace; - trailingWhitespaceLength = 0; - } - if (drawableTextRun.Size.Height > height) { height = drawableTextRun.Size.Height; @@ -1476,6 +1463,32 @@ namespace Avalonia.Media.TextFormatting } } + width = widthIncludingWhitespace; + + for (var i = _textRuns.Length - 1; i >= 0; i--) + { + var currentRun = _textRuns[i]; + + if(currentRun is ShapedTextRun shapedText) + { + var glyphRun = shapedText.GlyphRun; + var glyphRunMetrics = glyphRun.Metrics; + + newLineLength += glyphRunMetrics.NewLineLength; + + if (glyphRunMetrics.TrailingWhitespaceLength == 0) + { + break; + } + + trailingWhitespaceLength += glyphRunMetrics.TrailingWhitespaceLength; + + var whitespaceWidth = glyphRun.Size.Width - glyphRunMetrics.Width; + + width -= whitespaceWidth; + } + } + var start = GetParagraphOffsetX(width, widthIncludingWhitespace); if (!double.IsNaN(lineHeight) && !MathUtilities.IsZero(lineHeight)) @@ -1543,7 +1556,7 @@ namespace Avalonia.Media.TextFormatting return Math.Max(0, start); case TextAlignment.Right: - return Math.Max(0, _paragraphWidth - widthIncludingTrailingWhitespace); + return Math.Max(0, _paragraphWidth - width); default: return 0; diff --git a/src/Avalonia.Base/Media/TextFormatting/TextTrailingCharacterEllipsis.cs b/src/Avalonia.Base/Media/TextFormatting/TextTrailingCharacterEllipsis.cs index ccae99cc75..8a6607bce2 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextTrailingCharacterEllipsis.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextTrailingCharacterEllipsis.cs @@ -12,10 +12,13 @@ /// Text used as collapsing symbol. /// Width in which collapsing is constrained to. /// Text run properties of ellipsis symbol. - public TextTrailingCharacterEllipsis(string ellipsis, double width, TextRunProperties textRunProperties) + /// The flow direction of the collapsed line. + public TextTrailingCharacterEllipsis(string ellipsis, double width, + TextRunProperties textRunProperties, FlowDirection flowDirection) { Width = width; Symbol = new TextCharacters(ellipsis, textRunProperties); + FlowDirection = flowDirection; } /// @@ -24,6 +27,8 @@ /// public override TextRun Symbol { get; } + public override FlowDirection FlowDirection { get; } + /// public override TextRun[]? Collapse(TextLine textLine) { diff --git a/src/Avalonia.Base/Media/TextFormatting/TextTrailingWordEllipsis.cs b/src/Avalonia.Base/Media/TextFormatting/TextTrailingWordEllipsis.cs index c622c76a60..5252766382 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextTrailingWordEllipsis.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextTrailingWordEllipsis.cs @@ -12,14 +12,17 @@ /// Text used as collapsing symbol. /// width in which collapsing is constrained to. /// text run properties of ellipsis symbol. + /// flow direction of the collapsed line. public TextTrailingWordEllipsis( string ellipsis, double width, - TextRunProperties textRunProperties + TextRunProperties textRunProperties, + FlowDirection flowDirection ) { Width = width; Symbol = new TextCharacters(ellipsis, textRunProperties); + FlowDirection = flowDirection; } /// @@ -28,6 +31,8 @@ /// public override TextRun Symbol { get; } + public override FlowDirection FlowDirection { get; } + /// public override TextRun[]? Collapse(TextLine textLine) { diff --git a/src/Avalonia.Base/Media/TextLeadingPrefixTrimming.cs b/src/Avalonia.Base/Media/TextLeadingPrefixTrimming.cs index 7ba25eb005..19e6a70357 100644 --- a/src/Avalonia.Base/Media/TextLeadingPrefixTrimming.cs +++ b/src/Avalonia.Base/Media/TextLeadingPrefixTrimming.cs @@ -15,7 +15,7 @@ namespace Avalonia.Media public override TextCollapsingProperties CreateCollapsingProperties(TextCollapsingCreateInfo createInfo) { - return new TextLeadingPrefixCharacterEllipsis(_ellipsis, _prefixLength, createInfo.Width, createInfo.TextRunProperties); + return new TextLeadingPrefixCharacterEllipsis(_ellipsis, _prefixLength, createInfo.Width, createInfo.TextRunProperties, createInfo.FlowDirection); } public override string ToString() diff --git a/src/Avalonia.Base/Media/TextTrailingTrimming.cs b/src/Avalonia.Base/Media/TextTrailingTrimming.cs index 2edbaabbc6..8a3c5aa397 100644 --- a/src/Avalonia.Base/Media/TextTrailingTrimming.cs +++ b/src/Avalonia.Base/Media/TextTrailingTrimming.cs @@ -17,10 +17,10 @@ namespace Avalonia.Media { if (_isWordBased) { - return new TextTrailingWordEllipsis(_ellipsis, createInfo.Width, createInfo.TextRunProperties); + return new TextTrailingWordEllipsis(_ellipsis, createInfo.Width, createInfo.TextRunProperties, createInfo.FlowDirection); } - return new TextTrailingCharacterEllipsis(_ellipsis, createInfo.Width, createInfo.TextRunProperties); + return new TextTrailingCharacterEllipsis(_ellipsis, createInfo.Width, createInfo.TextRunProperties, createInfo.FlowDirection); } public override string ToString() diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs index 8a2d4ecc6b..dc8744b292 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs @@ -457,7 +457,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting if (textLine.Width > 300 || currentHeight + textLine.Height > 240) { - textLine = textLine.Collapse(new TextTrailingWordEllipsis(TextTrimming.DefaultEllipsisChar, 300, defaultProperties)); + textLine = textLine.Collapse(new TextTrailingWordEllipsis(TextTrimming.DefaultEllipsisChar, 300, defaultProperties, FlowDirection.LeftToRight)); } currentHeight += textLine.Height; diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs index 70e74cdf83..bd64d72a4d 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs @@ -407,7 +407,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting Assert.False(textLine.HasCollapsed); - TextCollapsingProperties collapsingProperties = trimming.CreateCollapsingProperties(new TextCollapsingCreateInfo(width, defaultProperties)); + TextCollapsingProperties collapsingProperties = trimming.CreateCollapsingProperties(new TextCollapsingCreateInfo(width, defaultProperties, FlowDirection.LeftToRight)); var collapsedLine = textLine.Collapse(collapsingProperties);