|
|
@@ -184,6 +184,10 @@ namespace Avalonia.Media.TextFormatting
|
|
|
{
|
|
|
characterHit = shapedRun.GlyphRun.GetCharacterHitFromDistance(distance, out _);
|
|
|
|
|
|
+ var offset = Math.Max(0, currentPosition - shapedRun.Text.Start);
|
|
|
+
|
|
|
+ characterHit = new CharacterHit(characterHit.FirstCharacterIndex + offset, characterHit.TrailingLength);
|
|
|
+
|
|
|
break;
|
|
|
}
|
|
|
default:
|
|
|
@@ -215,9 +219,11 @@ namespace Avalonia.Media.TextFormatting
|
|
|
/// <inheritdoc/>
|
|
|
public override double GetDistanceFromCharacterHit(CharacterHit characterHit)
|
|
|
{
|
|
|
- var characterIndex = characterHit.FirstCharacterIndex + (characterHit.TrailingLength != 0 ? 1 : 0);
|
|
|
+ var isTrailingHit = characterHit.TrailingLength > 0;
|
|
|
+ var characterIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
|
|
|
var currentDistance = Start;
|
|
|
var currentPosition = FirstTextSourceIndex;
|
|
|
+ var remainingLength = characterIndex - FirstTextSourceIndex;
|
|
|
|
|
|
GlyphRun? lastRun = null;
|
|
|
|
|
|
@@ -242,8 +248,10 @@ namespace Avalonia.Media.TextFormatting
|
|
|
}
|
|
|
|
|
|
//Look for a hit in within the current run
|
|
|
- if (characterIndex >= textRun.Text.Start && characterIndex <= textRun.Text.Start + textRun.Text.Length)
|
|
|
+ if (currentPosition + remainingLength <= currentPosition + textRun.Text.Length)
|
|
|
{
|
|
|
+ characterHit = new CharacterHit(textRun.Text.Start + remainingLength);
|
|
|
+
|
|
|
var distance = currentRun.GetDistanceFromCharacterHit(characterHit);
|
|
|
|
|
|
return currentDistance + distance;
|
|
|
@@ -254,28 +262,27 @@ namespace Avalonia.Media.TextFormatting
|
|
|
{
|
|
|
if (_flowDirection == FlowDirection.LeftToRight && (lastRun == null || lastRun.IsLeftToRight))
|
|
|
{
|
|
|
- if (characterIndex <= textRun.Text.Start)
|
|
|
+ if (characterIndex <= currentPosition)
|
|
|
{
|
|
|
return currentDistance;
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- if (characterIndex == textRun.Text.Start)
|
|
|
+ if (characterIndex == currentPosition)
|
|
|
{
|
|
|
return currentDistance;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (characterIndex == textRun.Text.Start + textRun.Text.Length &&
|
|
|
- characterHit.TrailingLength > 0)
|
|
|
+ if (characterIndex == currentPosition + textRun.Text.Length && isTrailingHit)
|
|
|
{
|
|
|
return currentDistance + currentRun.Size.Width;
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- if (characterIndex == textRun.Text.Start)
|
|
|
+ if (characterIndex == currentPosition)
|
|
|
{
|
|
|
return currentDistance + currentRun.Size.Width;
|
|
|
}
|
|
|
@@ -286,20 +293,24 @@ namespace Avalonia.Media.TextFormatting
|
|
|
|
|
|
if (nextRun != null)
|
|
|
{
|
|
|
- if (characterHit.FirstCharacterIndex == textRun.Text.End &&
|
|
|
- nextRun.ShapedBuffer.IsLeftToRight)
|
|
|
+ if (nextRun.ShapedBuffer.IsLeftToRight)
|
|
|
{
|
|
|
- return currentDistance;
|
|
|
+ if (characterIndex == currentPosition + textRun.Text.Length)
|
|
|
+ {
|
|
|
+ return currentDistance;
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
- if (characterIndex > textRun.Text.End && nextRun.Text.End < textRun.Text.End)
|
|
|
+ else
|
|
|
{
|
|
|
- return currentDistance;
|
|
|
+ if (currentPosition + nextRun.Text.Length == characterIndex)
|
|
|
+ {
|
|
|
+ return currentDistance;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- if (characterIndex > textRun.Text.End)
|
|
|
+ if (characterIndex > currentPosition + textRun.Text.Length)
|
|
|
{
|
|
|
return currentDistance;
|
|
|
}
|
|
|
@@ -329,6 +340,12 @@ namespace Avalonia.Media.TextFormatting
|
|
|
//No hit hit found so we add the full width
|
|
|
currentDistance += textRun.Size.Width;
|
|
|
currentPosition += textRun.TextSourceLength;
|
|
|
+ remainingLength -= textRun.TextSourceLength;
|
|
|
+
|
|
|
+ if (remainingLength <= 0)
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
return currentDistance;
|
|
|
@@ -394,210 +411,299 @@ namespace Avalonia.Media.TextFormatting
|
|
|
return GetPreviousCaretCharacterHit(characterHit);
|
|
|
}
|
|
|
|
|
|
- public override IReadOnlyList<TextBounds> GetTextBounds(int firstTextSourceCharacterIndex, int textLength)
|
|
|
+ private IReadOnlyList<TextBounds> GetTextBoundsLeftToRight(int firstTextSourceIndex, int textLength)
|
|
|
{
|
|
|
- if (firstTextSourceCharacterIndex + textLength <= FirstTextSourceIndex)
|
|
|
- {
|
|
|
- return Array.Empty<TextBounds>();
|
|
|
- }
|
|
|
+ var characterIndex = firstTextSourceIndex + textLength;
|
|
|
|
|
|
var result = new List<TextBounds>(TextRuns.Count);
|
|
|
- var lastDirection = _flowDirection;
|
|
|
+ var lastDirection = FlowDirection.LeftToRight;
|
|
|
var currentDirection = lastDirection;
|
|
|
+
|
|
|
var currentPosition = FirstTextSourceIndex;
|
|
|
- var currentRect = Rect.Empty;
|
|
|
+ var remainingLength = textLength;
|
|
|
+
|
|
|
var startX = Start;
|
|
|
+ double currentWidth = 0;
|
|
|
+ var currentRect = Rect.Empty;
|
|
|
|
|
|
- //A portion of the line is covered.
|
|
|
for (var index = 0; index < TextRuns.Count; index++)
|
|
|
{
|
|
|
- var currentRun = TextRuns[index] as DrawableTextRun;
|
|
|
-
|
|
|
- if (currentRun is null)
|
|
|
+ if (TextRuns[index] is not DrawableTextRun currentRun)
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
- TextRun? nextRun = null;
|
|
|
-
|
|
|
- if (index + 1 < TextRuns.Count)
|
|
|
+ if (currentPosition + currentRun.TextSourceLength <= firstTextSourceIndex)
|
|
|
{
|
|
|
- nextRun = TextRuns[index + 1];
|
|
|
+ startX += currentRun.Size.Width;
|
|
|
+
|
|
|
+ currentPosition += currentRun.TextSourceLength;
|
|
|
+
|
|
|
+ continue;
|
|
|
}
|
|
|
|
|
|
- if (nextRun != null)
|
|
|
+ var characterLength = 0;
|
|
|
+ var endX = startX;
|
|
|
+
|
|
|
+ if (currentRun is ShapedTextCharacters currentShapedRun)
|
|
|
{
|
|
|
- switch (nextRun)
|
|
|
- {
|
|
|
- case ShapedTextCharacters when currentRun is ShapedTextCharacters:
|
|
|
- {
|
|
|
- if (nextRun.Text.Start < currentRun.Text.Start && firstTextSourceCharacterIndex + textLength < currentRun.Text.End)
|
|
|
- {
|
|
|
- goto skip;
|
|
|
- }
|
|
|
+ var offset = Math.Max(0, firstTextSourceIndex - currentPosition);
|
|
|
|
|
|
- if (currentRun.Text.Start >= firstTextSourceCharacterIndex + textLength)
|
|
|
- {
|
|
|
- goto skip;
|
|
|
- }
|
|
|
+ currentPosition += offset;
|
|
|
|
|
|
- if (currentRun.Text.Start > nextRun.Text.Start && currentRun.Text.Start < firstTextSourceCharacterIndex)
|
|
|
- {
|
|
|
- goto skip;
|
|
|
- }
|
|
|
+ var startIndex = currentRun.Text.Start + offset;
|
|
|
|
|
|
- if (currentRun.Text.End < firstTextSourceCharacterIndex)
|
|
|
- {
|
|
|
- goto skip;
|
|
|
- }
|
|
|
+ var endOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(
|
|
|
+ currentShapedRun.ShapedBuffer.IsLeftToRight ?
|
|
|
+ new CharacterHit(startIndex + remainingLength) :
|
|
|
+ new CharacterHit(startIndex));
|
|
|
|
|
|
- goto noop;
|
|
|
- }
|
|
|
- default:
|
|
|
- {
|
|
|
- goto noop;
|
|
|
- }
|
|
|
- }
|
|
|
+ endX += endOffset;
|
|
|
+
|
|
|
+ var startOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(
|
|
|
+ currentShapedRun.ShapedBuffer.IsLeftToRight ?
|
|
|
+ new CharacterHit(startIndex) :
|
|
|
+ new CharacterHit(startIndex + remainingLength));
|
|
|
+
|
|
|
+ startX += startOffset;
|
|
|
+
|
|
|
+ var endHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _);
|
|
|
+ var startHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(startOffset, out _);
|
|
|
+
|
|
|
+ characterLength = Math.Abs(endHit.FirstCharacterIndex + endHit.TrailingLength - startHit.FirstCharacterIndex - startHit.TrailingLength);
|
|
|
|
|
|
- skip:
|
|
|
+ currentDirection = currentShapedRun.ShapedBuffer.IsLeftToRight ?
|
|
|
+ FlowDirection.LeftToRight :
|
|
|
+ FlowDirection.RightToLeft;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if (currentPosition < firstTextSourceIndex)
|
|
|
{
|
|
|
startX += currentRun.Size.Width;
|
|
|
- currentPosition += currentRun.TextSourceLength;
|
|
|
}
|
|
|
|
|
|
- continue;
|
|
|
-
|
|
|
- noop:
|
|
|
+ if (currentPosition + currentRun.TextSourceLength <= characterIndex)
|
|
|
{
|
|
|
+ endX += currentRun.Size.Width;
|
|
|
+
|
|
|
+ characterLength = currentRun.TextSourceLength;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- var endX = startX;
|
|
|
- var endOffset = 0d;
|
|
|
+ if (endX < startX)
|
|
|
+ {
|
|
|
+ (endX, startX) = (startX, endX);
|
|
|
+ }
|
|
|
|
|
|
- switch (currentRun)
|
|
|
+ //Lines that only contain a linebreak need to be covered here
|
|
|
+ if(characterLength == 0)
|
|
|
{
|
|
|
- case ShapedTextCharacters shapedRun:
|
|
|
- {
|
|
|
- endOffset = shapedRun.GlyphRun.GetDistanceFromCharacterHit(
|
|
|
- shapedRun.ShapedBuffer.IsLeftToRight ?
|
|
|
- new CharacterHit(firstTextSourceCharacterIndex + textLength) :
|
|
|
- new CharacterHit(firstTextSourceCharacterIndex));
|
|
|
+ characterLength = NewLineLength;
|
|
|
+ }
|
|
|
|
|
|
- endX += endOffset;
|
|
|
+ var runwidth = endX - startX;
|
|
|
+ var currentRunBounds = new TextRunBounds(new Rect(startX, 0, runwidth, Height), currentPosition, characterLength, currentRun);
|
|
|
|
|
|
- var startOffset = shapedRun.GlyphRun.GetDistanceFromCharacterHit(
|
|
|
- shapedRun.ShapedBuffer.IsLeftToRight ?
|
|
|
- new CharacterHit(firstTextSourceCharacterIndex) :
|
|
|
- new CharacterHit(firstTextSourceCharacterIndex + textLength));
|
|
|
+ if (lastDirection == currentDirection && result.Count > 0 && MathUtilities.AreClose(currentRect.Right, startX))
|
|
|
+ {
|
|
|
+ currentRect = currentRect.WithWidth(currentWidth + runwidth);
|
|
|
|
|
|
- startX += startOffset;
|
|
|
+ var textBounds = result[result.Count - 1];
|
|
|
|
|
|
- var characterHit = shapedRun.GlyphRun.IsLeftToRight ?
|
|
|
- shapedRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _) :
|
|
|
- shapedRun.GlyphRun.GetCharacterHitFromDistance(startOffset, out _);
|
|
|
+ textBounds.Rectangle = currentRect;
|
|
|
|
|
|
- currentPosition = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
|
|
|
+ textBounds.TextRunBounds.Add(currentRunBounds);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ currentRect = currentRunBounds.Rectangle;
|
|
|
|
|
|
- currentDirection = shapedRun.ShapedBuffer.IsLeftToRight ?
|
|
|
- FlowDirection.LeftToRight :
|
|
|
- FlowDirection.RightToLeft;
|
|
|
+ result.Add(new TextBounds(currentRect, currentDirection, new List<TextRunBounds> { currentRunBounds }));
|
|
|
+ }
|
|
|
|
|
|
- if (nextRun is ShapedTextCharacters nextShaped)
|
|
|
- {
|
|
|
- if (shapedRun.ShapedBuffer.IsLeftToRight == nextShaped.ShapedBuffer.IsLeftToRight)
|
|
|
- {
|
|
|
- endOffset = nextShaped.GlyphRun.GetDistanceFromCharacterHit(
|
|
|
- nextShaped.ShapedBuffer.IsLeftToRight ?
|
|
|
- new CharacterHit(firstTextSourceCharacterIndex + textLength) :
|
|
|
- new CharacterHit(firstTextSourceCharacterIndex));
|
|
|
+ currentWidth += runwidth;
|
|
|
+ currentPosition += characterLength;
|
|
|
|
|
|
- index++;
|
|
|
+ if (currentDirection == FlowDirection.LeftToRight)
|
|
|
+ {
|
|
|
+ if (currentPosition > characterIndex)
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if (currentPosition <= firstTextSourceIndex)
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- endX += endOffset;
|
|
|
+ startX = endX;
|
|
|
+ lastDirection = currentDirection;
|
|
|
+ remainingLength -= characterLength;
|
|
|
|
|
|
- currentRun = nextShaped;
|
|
|
+ if (remainingLength <= 0)
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- if (nextShaped.ShapedBuffer.IsLeftToRight)
|
|
|
- {
|
|
|
- characterHit = nextShaped.GlyphRun.GetCharacterHitFromDistance(endOffset, out _);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
|
|
|
- currentPosition = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ private IReadOnlyList<TextBounds> GetTextBoundsRightToLeft(int firstTextSourceIndex, int textLength)
|
|
|
+ {
|
|
|
+ var characterIndex = firstTextSourceIndex + textLength;
|
|
|
|
|
|
- break;
|
|
|
- }
|
|
|
- default:
|
|
|
- {
|
|
|
- if (currentPosition + currentRun.TextSourceLength <= firstTextSourceCharacterIndex + textLength)
|
|
|
- {
|
|
|
- endX += currentRun.Size.Width;
|
|
|
- }
|
|
|
+ var result = new List<TextBounds>(TextRuns.Count);
|
|
|
+ var lastDirection = FlowDirection.LeftToRight;
|
|
|
+ var currentDirection = lastDirection;
|
|
|
|
|
|
- if (currentPosition < firstTextSourceCharacterIndex)
|
|
|
- {
|
|
|
- startX += currentRun.Size.Width;
|
|
|
- }
|
|
|
+ var currentPosition = FirstTextSourceIndex;
|
|
|
+ var remainingLength = textLength;
|
|
|
|
|
|
- currentPosition += currentRun.TextSourceLength;
|
|
|
+ var startX = Start + WidthIncludingTrailingWhitespace;
|
|
|
+ double currentWidth = 0;
|
|
|
+ var currentRect = Rect.Empty;
|
|
|
|
|
|
- break;
|
|
|
- }
|
|
|
+ for (var index = TextRuns.Count - 1; index >= 0; index--)
|
|
|
+ {
|
|
|
+ if (TextRuns[index] is not DrawableTextRun currentRun)
|
|
|
+ {
|
|
|
+ continue;
|
|
|
}
|
|
|
|
|
|
- if (endX < startX)
|
|
|
+ if (currentPosition + currentRun.TextSourceLength <= firstTextSourceIndex)
|
|
|
{
|
|
|
- (endX, startX) = (startX, endX);
|
|
|
+ startX -= currentRun.Size.Width;
|
|
|
+
|
|
|
+ currentPosition += currentRun.TextSourceLength;
|
|
|
+
|
|
|
+ continue;
|
|
|
}
|
|
|
|
|
|
- var width = endX - startX;
|
|
|
+ var characterLength = 0;
|
|
|
+ var endX = startX;
|
|
|
|
|
|
- if (!MathUtilities.IsZero(width))
|
|
|
+ if (currentRun is ShapedTextCharacters currentShapedRun)
|
|
|
{
|
|
|
- if (lastDirection == currentDirection && result.Count > 0 && MathUtilities.AreClose(currentRect.Right, startX))
|
|
|
- {
|
|
|
- currentRect = currentRect.WithWidth(currentRect.Width + width);
|
|
|
+ var offset = Math.Max(0, firstTextSourceIndex - currentPosition);
|
|
|
+
|
|
|
+ currentPosition += offset;
|
|
|
+
|
|
|
+ var startIndex = currentRun.Text.Start + offset;
|
|
|
+
|
|
|
+ var endOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(
|
|
|
+ currentShapedRun.ShapedBuffer.IsLeftToRight ?
|
|
|
+ new CharacterHit(startIndex + remainingLength) :
|
|
|
+ new CharacterHit(startIndex));
|
|
|
+
|
|
|
+ endX += endOffset - currentShapedRun.Size.Width;
|
|
|
+
|
|
|
+ var startOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(
|
|
|
+ currentShapedRun.ShapedBuffer.IsLeftToRight ?
|
|
|
+ new CharacterHit(startIndex) :
|
|
|
+ new CharacterHit(startIndex + remainingLength));
|
|
|
|
|
|
- var textBounds = new TextBounds(currentRect, currentDirection);
|
|
|
+ startX += startOffset - currentShapedRun.Size.Width;
|
|
|
|
|
|
- result[result.Count - 1] = textBounds;
|
|
|
+ var endHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _);
|
|
|
+ var startHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(startOffset, out _);
|
|
|
+
|
|
|
+ characterLength = Math.Abs(startHit.FirstCharacterIndex + startHit.TrailingLength - endHit.FirstCharacterIndex - endHit.TrailingLength);
|
|
|
+
|
|
|
+ currentDirection = currentShapedRun.ShapedBuffer.IsLeftToRight ?
|
|
|
+ FlowDirection.LeftToRight :
|
|
|
+ FlowDirection.RightToLeft;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if (currentPosition + currentRun.TextSourceLength <= characterIndex)
|
|
|
+ {
|
|
|
+ endX -= currentRun.Size.Width;
|
|
|
}
|
|
|
- else
|
|
|
+
|
|
|
+ if (currentPosition < firstTextSourceIndex)
|
|
|
{
|
|
|
+ startX -= currentRun.Size.Width;
|
|
|
+
|
|
|
+ characterLength = currentRun.TextSourceLength;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (endX < startX)
|
|
|
+ {
|
|
|
+ (endX, startX) = (startX, endX);
|
|
|
+ }
|
|
|
|
|
|
- currentRect = new Rect(startX, 0, width, Height);
|
|
|
+ //Lines that only contain a linebreak need to be covered here
|
|
|
+ if (characterLength == 0)
|
|
|
+ {
|
|
|
+ characterLength = NewLineLength;
|
|
|
+ }
|
|
|
|
|
|
- result.Add(new TextBounds(currentRect, currentDirection));
|
|
|
+ var runWidth = endX - startX;
|
|
|
+ var currentRunBounds = new TextRunBounds(new Rect(startX, 0, runWidth, Height), currentPosition, characterLength, currentRun);
|
|
|
|
|
|
- }
|
|
|
+ if (lastDirection == currentDirection && result.Count > 0 && MathUtilities.AreClose(currentRect.Right, startX))
|
|
|
+ {
|
|
|
+ currentRect = currentRect.WithWidth(currentWidth + runWidth);
|
|
|
+
|
|
|
+ var textBounds = result[result.Count - 1];
|
|
|
+
|
|
|
+ textBounds.Rectangle = currentRect;
|
|
|
+
|
|
|
+ textBounds.TextRunBounds.Add(currentRunBounds);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ currentRect = currentRunBounds.Rectangle;
|
|
|
+
|
|
|
+ result.Add(new TextBounds(currentRect, currentDirection, new List<TextRunBounds> { currentRunBounds }));
|
|
|
}
|
|
|
|
|
|
+ currentWidth += runWidth;
|
|
|
+ currentPosition += characterLength;
|
|
|
+
|
|
|
if (currentDirection == FlowDirection.LeftToRight)
|
|
|
{
|
|
|
- if (currentPosition > firstTextSourceCharacterIndex + textLength)
|
|
|
+ if (currentPosition > characterIndex)
|
|
|
{
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- if (currentPosition <= firstTextSourceCharacterIndex)
|
|
|
+ if (currentPosition <= firstTextSourceIndex)
|
|
|
{
|
|
|
break;
|
|
|
}
|
|
|
-
|
|
|
- endX += currentRun.Size.Width - endOffset;
|
|
|
}
|
|
|
|
|
|
lastDirection = currentDirection;
|
|
|
- startX = endX;
|
|
|
+ remainingLength -= characterLength;
|
|
|
+
|
|
|
+ if (remainingLength <= 0)
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
+ public override IReadOnlyList<TextBounds> GetTextBounds(int firstTextSourceIndex, int textLength)
|
|
|
+ {
|
|
|
+ if (_paragraphProperties.FlowDirection == FlowDirection.LeftToRight)
|
|
|
+ {
|
|
|
+ return GetTextBoundsLeftToRight(firstTextSourceIndex, textLength);
|
|
|
+ }
|
|
|
+
|
|
|
+ return GetTextBoundsRightToLeft(firstTextSourceIndex, textLength);
|
|
|
+ }
|
|
|
+
|
|
|
public TextLineImpl FinalizeLine()
|
|
|
{
|
|
|
_textLineMetrics = CreateLineMetrics();
|