|
|
|
@ -1,5 +1,6 @@ |
|
|
|
using System; |
|
|
|
using System.Collections.Generic; |
|
|
|
using Avalonia.Media.TextFormatting.Unicode; |
|
|
|
using Avalonia.Utilities; |
|
|
|
|
|
|
|
namespace Avalonia.Media.TextFormatting |
|
|
|
@ -708,11 +709,121 @@ namespace Avalonia.Media.TextFormatting |
|
|
|
{ |
|
|
|
_textLineMetrics = CreateLineMetrics(); |
|
|
|
|
|
|
|
Justify(); |
|
|
|
|
|
|
|
BidiReorder(); |
|
|
|
|
|
|
|
return this; |
|
|
|
} |
|
|
|
|
|
|
|
private void Justify() |
|
|
|
{ |
|
|
|
if (_paragraphProperties.TextAlignment != TextAlignment.Justify) |
|
|
|
{ |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
var paragraphWidth = _paragraphWidth; |
|
|
|
|
|
|
|
if (double.IsInfinity(paragraphWidth)) |
|
|
|
{ |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if(_textLineMetrics.NewLineLength > 0) |
|
|
|
{ |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if(TextLineBreak is not null && TextLineBreak.TextEndOfLine is not null) |
|
|
|
{ |
|
|
|
if(TextLineBreak.RemainingRuns is null || TextLineBreak.RemainingRuns.Count == 0) |
|
|
|
{ |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
var breakOportunities = new Queue<int>(); |
|
|
|
|
|
|
|
foreach (var textRun in TextRuns) |
|
|
|
{ |
|
|
|
var text = textRun.Text; |
|
|
|
|
|
|
|
if (text.IsEmpty) |
|
|
|
{ |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
var start = text.Start; |
|
|
|
|
|
|
|
var lineBreakEnumerator = new LineBreakEnumerator(text); |
|
|
|
|
|
|
|
while (lineBreakEnumerator.MoveNext()) |
|
|
|
{ |
|
|
|
var currentBreak = lineBreakEnumerator.Current; |
|
|
|
|
|
|
|
if (!currentBreak.Required && currentBreak.PositionWrap != text.Length) |
|
|
|
{ |
|
|
|
breakOportunities.Enqueue(start + currentBreak.PositionMeasure); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if(breakOportunities.Count == 0) |
|
|
|
{ |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
var remainingSpace = Math.Max(0, paragraphWidth - WidthIncludingTrailingWhitespace); |
|
|
|
var spacing = remainingSpace / breakOportunities.Count; |
|
|
|
|
|
|
|
foreach (var textRun in TextRuns) |
|
|
|
{ |
|
|
|
var text = textRun.Text; |
|
|
|
|
|
|
|
if (text.IsEmpty) |
|
|
|
{ |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
if(textRun is ShapedTextCharacters shapedText) |
|
|
|
{ |
|
|
|
var glyphRun = shapedText.GlyphRun; |
|
|
|
var shapedBuffer = shapedText.ShapedBuffer; |
|
|
|
var currentPosition = text.Start; |
|
|
|
|
|
|
|
while(breakOportunities.Count > 0) |
|
|
|
{ |
|
|
|
var characterIndex = breakOportunities.Dequeue(); |
|
|
|
|
|
|
|
if (characterIndex < currentPosition) |
|
|
|
{ |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
var glyphIndex = glyphRun.FindGlyphIndex(characterIndex); |
|
|
|
var glyphInfo = shapedBuffer.GlyphInfos[glyphIndex]; |
|
|
|
|
|
|
|
shapedBuffer.GlyphInfos[glyphIndex] = new GlyphInfo(glyphInfo.GlyphIndex, glyphInfo.GlyphCluster, glyphInfo.GlyphAdvance + spacing); |
|
|
|
} |
|
|
|
|
|
|
|
glyphRun.GlyphAdvances = shapedBuffer.GlyphAdvances; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
var trailingWhitespaceWidth = _textLineMetrics.WidthIncludingTrailingWhitespace - _textLineMetrics.Width; |
|
|
|
|
|
|
|
_textLineMetrics = new TextLineMetrics( |
|
|
|
_textLineMetrics.HasOverflowed, |
|
|
|
_textLineMetrics.Height, |
|
|
|
_textLineMetrics.NewLineLength, |
|
|
|
_textLineMetrics.Start, |
|
|
|
_textLineMetrics.TextBaseline, |
|
|
|
_textLineMetrics.TrailingWhitespaceLength, |
|
|
|
paragraphWidth - trailingWhitespaceWidth, |
|
|
|
paragraphWidth); |
|
|
|
} |
|
|
|
|
|
|
|
private static sbyte GetRunBidiLevel(DrawableTextRun run, FlowDirection flowDirection) |
|
|
|
{ |
|
|
|
if (run is ShapedTextCharacters shapedTextCharacters) |
|
|
|
|