Browse Source

Fix GetTextBounds for mixed runs

pull/8053/head
Benedikt Stebner 4 years ago
parent
commit
bdadb6a351
  1. 47
      src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
  2. 7
      src/Skia/Avalonia.Skia/TextShaperImpl.cs
  3. 33
      src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs
  4. BIN
      tests/TestFiles/Direct2D1/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png
  5. BIN
      tests/TestFiles/Skia/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png

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

@ -536,6 +536,11 @@ namespace Avalonia.Media.TextFormatting
endX += currentRun.Size.Width; endX += currentRun.Size.Width;
} }
if(currentPosition < firstTextSourceCharacterIndex)
{
startX += currentRun.Size.Width;
}
currentPosition += currentRun.TextSourceLength; currentPosition += currentRun.TextSourceLength;
break; break;
@ -590,10 +595,10 @@ namespace Avalonia.Media.TextFormatting
public TextLineImpl FinalizeLine() public TextLineImpl FinalizeLine()
{ {
BidiReorder();
_textLineMetrics = CreateLineMetrics(); _textLineMetrics = CreateLineMetrics();
BidiReorder();
return this; return this;
} }
@ -1068,41 +1073,11 @@ namespace Avalonia.Media.TextFormatting
} }
} }
switch (_paragraphProperties.FlowDirection) if (index == _textRuns.Count - 1)
{ {
case FlowDirection.LeftToRight: width = widthIncludingWhitespace + textRun.GlyphRun.Metrics.Width;
{ trailingWhitespaceLength = textRun.GlyphRun.Metrics.TrailingWhitespaceLength;
if (index == _textRuns.Count - 1) newLineLength = textRun.GlyphRun.Metrics.NewlineLength;
{
width = widthIncludingWhitespace + textRun.GlyphRun.Metrics.Width;
trailingWhitespaceLength = textRun.GlyphRun.Metrics.TrailingWhitespaceLength;
newLineLength = textRun.GlyphRun.Metrics.NewlineLength;
}
break;
}
case FlowDirection.RightToLeft:
{
if (index == _textRuns.Count - 1)
{
var firstRun = _textRuns[0];
if (firstRun is ShapedTextCharacters shapedTextCharacters)
{
var offset = shapedTextCharacters.GlyphRun.Metrics.WidthIncludingTrailingWhitespace -
shapedTextCharacters.GlyphRun.Metrics.Width;
width = widthIncludingWhitespace +
textRun.GlyphRun.Metrics.WidthIncludingTrailingWhitespace - offset;
trailingWhitespaceLength = shapedTextCharacters.GlyphRun.Metrics.TrailingWhitespaceLength;
newLineLength = shapedTextCharacters.GlyphRun.Metrics.NewlineLength;
}
}
break;
}
} }
widthIncludingWhitespace += textRun.GlyphRun.Metrics.WidthIncludingTrailingWhitespace; widthIncludingWhitespace += textRun.GlyphRun.Metrics.WidthIncludingTrailingWhitespace;

7
src/Skia/Avalonia.Skia/TextShaperImpl.cs

@ -27,7 +27,7 @@ namespace Avalonia.Skia
buffer.GuessSegmentProperties(); buffer.GuessSegmentProperties();
buffer.Direction = (bidiLevel & 1) == 0 ? Direction.LeftToRight : Direction.RightToLeft; buffer.Direction = Direction.LeftToRight; //Always shape LeftToRight
buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture); buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture);
@ -35,11 +35,6 @@ namespace Avalonia.Skia
font.Shape(buffer); font.Shape(buffer);
if (buffer.Direction == Direction.RightToLeft)
{
buffer.Reverse();
}
font.GetScale(out var scaleX, out _); font.GetScale(out var scaleX, out _);
var textScale = fontRenderingEmSize / scaleX; var textScale = fontRenderingEmSize / scaleX;

33
src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs

@ -1,6 +1,5 @@
using System; using System;
using System.Globalization; using System.Globalization;
using Avalonia.Media;
using Avalonia.Media.TextFormatting; using Avalonia.Media.TextFormatting;
using Avalonia.Media.TextFormatting.Unicode; using Avalonia.Media.TextFormatting.Unicode;
using Avalonia.Platform; using Avalonia.Platform;
@ -11,8 +10,7 @@ using GlyphInfo = HarfBuzzSharp.GlyphInfo;
namespace Avalonia.Direct2D1.Media namespace Avalonia.Direct2D1.Media
{ {
internal class TextShaperImpl : ITextShaperImpl
internal class TextShaperImpl : ITextShaperImpl
{ {
public ShapedBuffer ShapeText(ReadOnlySlice<char> text, TextShaperOptions options) public ShapedBuffer ShapeText(ReadOnlySlice<char> text, TextShaperOptions options)
{ {
@ -23,25 +21,20 @@ internal class TextShaperImpl : ITextShaperImpl
using (var buffer = new Buffer()) using (var buffer = new Buffer())
{ {
buffer.AddUtf16(text.Buffer.Span, text.Start, text.Length); buffer.AddUtf16(text.Buffer.Span, text.BufferOffset, text.Length);
MergeBreakPair(buffer); MergeBreakPair(buffer);
buffer.GuessSegmentProperties(); buffer.GuessSegmentProperties();
buffer.Direction = (bidiLevel & 1) == 0 ? Direction.LeftToRight : Direction.RightToLeft; buffer.Direction = Direction.LeftToRight; //Always shape LeftToRight
buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture); buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture);
var font = ((GlyphTypefaceImpl)typeface.PlatformImpl).Font; var font = ((GlyphTypefaceImpl)typeface.PlatformImpl).Font;
font.Shape(buffer); font.Shape(buffer);
if (buffer.Direction == Direction.RightToLeft)
{
buffer.Reverse();
}
font.GetScale(out var scaleX, out _); font.GetScale(out var scaleX, out _);
var textScale = fontRenderingEmSize / scaleX; var textScale = fontRenderingEmSize / scaleX;
@ -60,13 +53,13 @@ internal class TextShaperImpl : ITextShaperImpl
var glyphIndex = (ushort)sourceInfo.Codepoint; var glyphIndex = (ushort)sourceInfo.Codepoint;
var glyphCluster = (int)sourceInfo.Cluster; var glyphCluster = (int)(sourceInfo.Cluster);
var glyphAdvance = GetGlyphAdvance(glyphPositions, i, textScale); var glyphAdvance = GetGlyphAdvance(glyphPositions, i, textScale);
var glyphOffset = GetGlyphOffset(glyphPositions, i, textScale); var glyphOffset = GetGlyphOffset(glyphPositions, i, textScale);
if (glyphIndex == 0 && text[glyphCluster] == '\t') if (glyphIndex == 0 && text.Buffer.Span[glyphCluster] == '\t')
{ {
glyphIndex = typeface.GetGlyph(' '); glyphIndex = typeface.GetGlyph(' ');
@ -75,9 +68,7 @@ internal class TextShaperImpl : ITextShaperImpl
4 * typeface.GetGlyphAdvance(glyphIndex) * textScale; 4 * typeface.GetGlyphAdvance(glyphIndex) * textScale;
} }
var targetInfo = var targetInfo = new Avalonia.Media.TextFormatting.GlyphInfo(glyphIndex, glyphCluster, glyphAdvance, glyphOffset);
new Avalonia.Media.TextFormatting.GlyphInfo(glyphIndex, glyphCluster, glyphAdvance,
glyphOffset);
shapedBuffer[i] = targetInfo; shapedBuffer[i] = targetInfo;
} }
@ -91,7 +82,7 @@ internal class TextShaperImpl : ITextShaperImpl
var length = buffer.Length; var length = buffer.Length;
var glyphInfos = buffer.GetGlyphInfoSpan(); var glyphInfos = buffer.GetGlyphInfoSpan();
var second = glyphInfos[length - 1]; var second = glyphInfos[length - 1];
if (!new Codepoint((int)second.Codepoint).IsBreakChar) if (!new Codepoint((int)second.Codepoint).IsBreakChar)
@ -102,7 +93,7 @@ internal class TextShaperImpl : ITextShaperImpl
if (length > 1 && glyphInfos[length - 2].Codepoint == '\r' && second.Codepoint == '\n') if (length > 1 && glyphInfos[length - 2].Codepoint == '\r' && second.Codepoint == '\n')
{ {
var first = glyphInfos[length - 2]; var first = glyphInfos[length - 2];
first.Codepoint = '\u200C'; first.Codepoint = '\u200C';
second.Codepoint = '\u200C'; second.Codepoint = '\u200C';
second.Cluster = first.Cluster; second.Cluster = first.Cluster;
@ -113,7 +104,7 @@ internal class TextShaperImpl : ITextShaperImpl
{ {
*p = first; *p = first;
} }
fixed (GlyphInfo* p = &glyphInfos[length - 1]) fixed (GlyphInfo* p = &glyphInfos[length - 1])
{ {
*p = second; *p = second;
@ -148,7 +139,7 @@ internal class TextShaperImpl : ITextShaperImpl
private static double GetGlyphAdvance(ReadOnlySpan<GlyphPosition> glyphPositions, int index, double textScale) private static double GetGlyphAdvance(ReadOnlySpan<GlyphPosition> glyphPositions, int index, double textScale)
{ {
// Depends on direction of layout // Depends on direction of layout
// advanceBuffer[index] = buffer.GlyphPositions[index].YAdvance * textScale; // glyphPositions[index].YAdvance * textScale;
return glyphPositions[index].XAdvance * textScale; return glyphPositions[index].XAdvance * textScale;
} }
} }

BIN
tests/TestFiles/Direct2D1/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 768 B

After

Width:  |  Height:  |  Size: 752 B

BIN
tests/TestFiles/Skia/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 532 B

After

Width:  |  Height:  |  Size: 557 B

Loading…
Cancel
Save