From bdadb6a35111899747c353aa01a1ab334805a384 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Tue, 3 May 2022 12:04:26 +0200 Subject: [PATCH] Fix GetTextBounds for mixed runs --- .../Media/TextFormatting/TextLineImpl.cs | 47 ++++-------------- src/Skia/Avalonia.Skia/TextShaperImpl.cs | 7 +-- .../Media/TextShaperImpl.cs | 33 +++++------- ...estrictedHeight_VerticalAlign.expected.png | Bin 768 -> 752 bytes ...estrictedHeight_VerticalAlign.expected.png | Bin 532 -> 557 bytes 5 files changed, 24 insertions(+), 63 deletions(-) diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs index 35ada3c7ee..a518e2ffb8 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs @@ -536,6 +536,11 @@ namespace Avalonia.Media.TextFormatting endX += currentRun.Size.Width; } + if(currentPosition < firstTextSourceCharacterIndex) + { + startX += currentRun.Size.Width; + } + currentPosition += currentRun.TextSourceLength; break; @@ -590,10 +595,10 @@ namespace Avalonia.Media.TextFormatting public TextLineImpl FinalizeLine() { - BidiReorder(); - _textLineMetrics = CreateLineMetrics(); + BidiReorder(); + return this; } @@ -1068,41 +1073,11 @@ namespace Avalonia.Media.TextFormatting } } - switch (_paragraphProperties.FlowDirection) + if (index == _textRuns.Count - 1) { - case FlowDirection.LeftToRight: - { - if (index == _textRuns.Count - 1) - { - 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; - } + width = widthIncludingWhitespace + textRun.GlyphRun.Metrics.Width; + trailingWhitespaceLength = textRun.GlyphRun.Metrics.TrailingWhitespaceLength; + newLineLength = textRun.GlyphRun.Metrics.NewlineLength; } widthIncludingWhitespace += textRun.GlyphRun.Metrics.WidthIncludingTrailingWhitespace; diff --git a/src/Skia/Avalonia.Skia/TextShaperImpl.cs b/src/Skia/Avalonia.Skia/TextShaperImpl.cs index ebaa247da8..908b0ffa47 100644 --- a/src/Skia/Avalonia.Skia/TextShaperImpl.cs +++ b/src/Skia/Avalonia.Skia/TextShaperImpl.cs @@ -27,7 +27,7 @@ namespace Avalonia.Skia 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); @@ -35,11 +35,6 @@ namespace Avalonia.Skia font.Shape(buffer); - if (buffer.Direction == Direction.RightToLeft) - { - buffer.Reverse(); - } - font.GetScale(out var scaleX, out _); var textScale = fontRenderingEmSize / scaleX; diff --git a/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs index 59027a663f..f4e4b00147 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs @@ -1,6 +1,5 @@ using System; using System.Globalization; -using Avalonia.Media; using Avalonia.Media.TextFormatting; using Avalonia.Media.TextFormatting.Unicode; using Avalonia.Platform; @@ -11,8 +10,7 @@ using GlyphInfo = HarfBuzzSharp.GlyphInfo; namespace Avalonia.Direct2D1.Media { - -internal class TextShaperImpl : ITextShaperImpl + internal class TextShaperImpl : ITextShaperImpl { public ShapedBuffer ShapeText(ReadOnlySlice text, TextShaperOptions options) { @@ -23,25 +21,20 @@ internal class TextShaperImpl : ITextShaperImpl 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); - + 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; font.Shape(buffer); - if (buffer.Direction == Direction.RightToLeft) - { - buffer.Reverse(); - } - font.GetScale(out var scaleX, out _); var textScale = fontRenderingEmSize / scaleX; @@ -60,13 +53,13 @@ internal class TextShaperImpl : ITextShaperImpl var glyphIndex = (ushort)sourceInfo.Codepoint; - var glyphCluster = (int)sourceInfo.Cluster; + var glyphCluster = (int)(sourceInfo.Cluster); var glyphAdvance = GetGlyphAdvance(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(' '); @@ -75,9 +68,7 @@ internal class TextShaperImpl : ITextShaperImpl 4 * typeface.GetGlyphAdvance(glyphIndex) * textScale; } - var targetInfo = - new Avalonia.Media.TextFormatting.GlyphInfo(glyphIndex, glyphCluster, glyphAdvance, - glyphOffset); + var targetInfo = new Avalonia.Media.TextFormatting.GlyphInfo(glyphIndex, glyphCluster, glyphAdvance, glyphOffset); shapedBuffer[i] = targetInfo; } @@ -91,7 +82,7 @@ internal class TextShaperImpl : ITextShaperImpl var length = buffer.Length; var glyphInfos = buffer.GetGlyphInfoSpan(); - + var second = glyphInfos[length - 1]; 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') { var first = glyphInfos[length - 2]; - + first.Codepoint = '\u200C'; second.Codepoint = '\u200C'; second.Cluster = first.Cluster; @@ -113,7 +104,7 @@ internal class TextShaperImpl : ITextShaperImpl { *p = first; } - + fixed (GlyphInfo* p = &glyphInfos[length - 1]) { *p = second; @@ -148,7 +139,7 @@ internal class TextShaperImpl : ITextShaperImpl private static double GetGlyphAdvance(ReadOnlySpan glyphPositions, int index, double textScale) { // Depends on direction of layout - // advanceBuffer[index] = buffer.GlyphPositions[index].YAdvance * textScale; + // glyphPositions[index].YAdvance * textScale; return glyphPositions[index].XAdvance * textScale; } } diff --git a/tests/TestFiles/Direct2D1/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png b/tests/TestFiles/Direct2D1/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png index edd4dfd263d0aeddca97eb4f38b4a5310ef6441f..e8624fa457f37565fdc483c474424991e7b696d3 100644 GIT binary patch delta 663 zcmV;I0%-k!2Ji)tK~kDYL_t(|UhUgIa??N*$8nhhgpv-9Kst&PbZ~-{bWo>HI%eP$ zbliY0bTFi#WQHbHCZVECE@0mCYC&Ev)~@tc(Vy>U=9gVr&L&sF5? z2a=zgt*ZJWIdk3Ow4gWZj_kMw*zr#C&2{Yze1z^>+>Eo|l*ZA6u^yCDYTA#}F7b|b z1-+DUQlBp7XTR-n+9lr6_JMtu-Y0j%e~v?mF0?|+c*2;U(Dpd(9@K4;4-y?YmRFK4 z^g(@eT|_WHMd&L`zBFy;-KXYy|(KU{b8zDnNKxsvFW zH-0;ezHL90^u_#aD^}3IBtx9|jEVjQPyh0jnrD(F8Df4|Lt|DbXw1sJ6tu3b_2LwN zfr_3W`0X%Otk(2a znEu5s`c3DXRyVzJM1r%nkRJ0aMM(FjDeD5@R(^af#<|TF^PCdjI796AKN{2Wx1| x3I&Z>NnUc)osL*?lhFbeli&gb8GzoZ>JL{KmA-v_Vov}7002ovPDHLkV1g47Q*QtO delta 680 zcmV;Z0$2U;1%L*SK~kzoL_t(|UhUg4a??N*#&MYggpv-903AgNIyeCxbfl&a9W!tW zI&MH0Iv6M@nW3S|1S(3nfO#)Z%WR}I+O^(-y!!uUzG!9H8)yBZ?8;6DlfeQOe*gdg zXxpaWPS|ey#tfUx?*;5HyP&tZcTL!I`ujjZqdp26OL9YU+nOU6lCRd>je^FKJd`}O zW_MF^B>8f=*LCkC {7WCyrdau|9DDpz`!J7I0!x`G6xaq6RA@-#wV?8P7*0lB2 zcR0njpx4+}$;-od?LS_b&&a;ve=BL7*bO6(B(JSGl6vx=r1i#b7`Y?S$yCbPULeM6 zTHMz){gUg6L?5a-l^jR=yNWr7KJht^UmM12HvJ8fFHxkRw^Gof+f%VkP`9s=*e0mk z7vps`wh8=x#-cMc>d`3$^6eTg3VIo*Bf3vR=Kl|;! z(DHaSQP7@?)mjVRC24uQm~x;g)e2hQi1%9P=%2M8);uWsoOPb3`6l@l?GNK=&Er+A zpubDJ=7suZ&Sz^Ll=qV7ea`gjQ**ChgxBkvx2!pv=VH~fuM&*cZ0&rgMIUtbr>&cyZeJy_O~80f#WsdHG}g%XEHOVp z>R4%l)Unb8sbi%HQpZXYq>i0Rjt95J(bw-R-tWct3GwW!G(|iMC>jgkxg4sswAxF~ z1qq=Aoq5-UtN(5T)WaMaO`)LClwW{S>gC-`V7Q6^ O0000Wy2)Tif!}f*BA9YQBr<>Yv<0+ed$LhGhl$6qoFtRV$DAqv>tEFoHy;= zjj8AUHLmvgqjP}4f0`Ym-l@|ncRSzK*B<`-V%Iyxa-Mt3j*FJP@}0!LwOcIxMdrlE z`EBc_9=&?AZmz%QzU4W~PG3(r%l<3q)s%g)(bchAU-i5`|6g~~!uOv(cvj{7-f@I; zZO^|R^Na=Arn~kV?eHzSTz#h~bwk)M4ct2k#G&)%(W*Dsx)C6j$|eyBU2 z#3_f(+TH&xR=QqWGxcS(?x8Q~dmJTy>788NygzpSjqgWi)>y6GoVM2Iepc=R`^UZi z3We58xyrynyb(IzS%pj{N(kv(qntpW!+KP9r*m+Cc8U!>XmlI yb8njNTQ2ZS)p@fv+pJj4eL2tLs~9ojqI);H&U7`F+XGKFP=N&RV07srr_IdAW5%)1;Q!uDXlX#W9ggKvom4qEyL4~4P0&0{{ck2_jY zz?fNE$h%Z>(W=lXQ?j31ZCk(J|C5mA@slS#KgDbNF)^S4hwFN;%CwgrT&UI0`gre( zyBn{Z`^PAr`Jt|XYkILgL&iym%_{3BPpMbF-QIRwbLpF=FDusivZW`ln|z07Z}8Ve zAHMMX-Jp@P^FYYz`%&?e|M>j%d7pLG=tA@w(@X6Ni@OMP+s%{|8UZRef2 zOxJB?{S~_W*Ou5%Gj2?N7|#)W?H1GDRgZPpV+}8q9Cgq?Fkj{U;Tz2DaF{%vno$(gm^S9kO54e|f1`v3Z(cq0>#J*i*%C*Mn*p3gV8zGmi= zcRgFP)>dv@6<_N1-{NIR@src0`e9#J_nmMI=P`-C@@dwg