浏览代码

Fixes #3873. TextFormatter isn't properly handling combining marks on alignments. (#3874)

Co-authored-by: Tig <[email protected]>
BDisp 7 月之前
父节点
当前提交
dbfe521db3
共有 2 个文件被更改,包括 90 次插入6 次删除
  1. 6 6
      Terminal.Gui/Text/TextFormatter.cs
  2. 84 0
      UnitTests/Text/TextFormatterTests.cs

+ 6 - 6
Terminal.Gui/Text/TextFormatter.cs

@@ -1411,7 +1411,7 @@ public class TextFormatter
 
                 if (textFormatter is { Alignment: Alignment.Center })
                 {
-                    return GetRangeThatFits (runes, Math.Max ((runes.Count - width) / 2, 0), text, width, tabWidth, textDirection);
+                    return GetRangeThatFits (runes, Math.Max ((runes.Count - width - zeroLength) / 2, 0), text, width, tabWidth, textDirection);
                 }
 
                 return GetRangeThatFits (runes, 0, text, width, tabWidth, textDirection);
@@ -1426,7 +1426,7 @@ public class TextFormatter
 
                 if (textFormatter is { VerticalAlignment: Alignment.Center })
                 {
-                    return GetRangeThatFits (runes, Math.Max ((runes.Count - width) / 2, 0), text, width, tabWidth, textDirection);
+                    return GetRangeThatFits (runes, Math.Max ((runes.Count - width - zeroLength) / 2, 0), text, width, tabWidth, textDirection);
                 }
 
                 return GetRangeThatFits (runes, 0, text, width, tabWidth, textDirection);
@@ -1451,7 +1451,7 @@ public class TextFormatter
             }
             else if (textFormatter is { Alignment: Alignment.Center })
             {
-                return GetRangeThatFits (runes, Math.Max ((runes.Count - width) / 2, 0), text, width, tabWidth, textDirection);
+                return GetRangeThatFits (runes, Math.Max ((runes.Count - width - zeroLength) / 2, 0), text, width, tabWidth, textDirection);
             }
             else if (GetRuneWidth (text, tabWidth, textDirection) > width)
             {
@@ -1470,7 +1470,7 @@ public class TextFormatter
             }
             else if (textFormatter is { VerticalAlignment: Alignment.Center })
             {
-                return GetRangeThatFits (runes, Math.Max ((runes.Count - width) / 2, 0), text, width, tabWidth, textDirection);
+                return GetRangeThatFits (runes, Math.Max ((runes.Count - width - zeroLength) / 2, 0), text, width, tabWidth, textDirection);
             }
             else if (runes.Count - zeroLength > width)
             {
@@ -1526,7 +1526,7 @@ public class TextFormatter
         }
         else
         {
-            textCount = words.Sum (arg => arg.GetRuneCount ());
+            textCount = words.Sum (arg => arg.GetRuneCount ()) - text.EnumerateRunes ().Sum (r => r.GetColumns () == 0 ? 1 : 0);
         }
 
         int spaces = words.Length > 1 ? (width - textCount) / (words.Length - 1) : 0;
@@ -1936,7 +1936,7 @@ public class TextFormatter
 
     private static int GetRuneWidth (Rune rune, int tabWidth, TextDirection textDirection = TextDirection.LeftRight_TopBottom)
     {
-        int runeWidth = IsHorizontalDirection (textDirection) ? rune.GetColumns () : 1;
+        int runeWidth = IsHorizontalDirection (textDirection) ? rune.GetColumns () : rune.GetColumns () == 0 ? 0 : 1;
 
         if (rune.Value == '\t')
         {

+ 84 - 0
UnitTests/Text/TextFormatterTests.cs

@@ -4629,6 +4629,90 @@ ssb
         Assert.Equal (expectedWrappedText, wrappedText);
     }
 
+    [Theory]
+    [InlineData (
+                    "Les Mise\u0301rables",
+                    14,
+                    -1,
+                    false,
+                    new [] { "Les Misérables" },
+                    "Les Misérables"
+                )]
+    [InlineData (
+                    "Les Mise\u0328\u0301rables",
+                    14,
+                    -2,
+                    false,
+                    new [] { "Les Misę́rables" },
+                    "Les Misę́rables"
+                )]
+    public void Format_Combining_Marks_Alignments (
+        string text,
+        int maxWidth,
+        int widthOffset,
+        bool wrap,
+        IEnumerable<string> resultLines,
+        string expectedText
+    )
+    {
+        Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
+
+        // Horizontal text direction
+        foreach (Alignment alignment in Enum.GetValues (typeof (Alignment)))
+        {
+            TextFormatter tf = new () { Text = text, ConstrainToSize = new (maxWidth, 1), WordWrap = wrap, Alignment = alignment };
+
+            List<string> list = TextFormatter.Format (
+                                                      text,
+                                                      maxWidth,
+                                                      alignment,
+                                                      wrap,
+                                                      tf.PreserveTrailingSpaces,
+                                                      tf.TabWidth,
+                                                      tf.Direction,
+                                                      tf.MultiLine,
+                                                      tf);
+            Assert.Equal (list.Count, resultLines.Count ());
+            Assert.Equal (resultLines, list);
+            var formattedText = string.Empty;
+
+            foreach (string txt in list)
+            {
+                formattedText += txt;
+            }
+
+            Assert.Equal (expectedText, formattedText);
+        }
+
+        // Vertical text direction
+        foreach (Alignment alignment in Enum.GetValues (typeof (Alignment)))
+        {
+            TextFormatter tf = new ()
+                { Text = text, ConstrainToSize = new (1, maxWidth), WordWrap = wrap, VerticalAlignment = alignment, Direction = TextDirection.TopBottom_LeftRight };
+
+            List<string> list = TextFormatter.Format (
+                                                      text,
+                                                      maxWidth,
+                                                      alignment,
+                                                      wrap,
+                                                      tf.PreserveTrailingSpaces,
+                                                      tf.TabWidth,
+                                                      tf.Direction,
+                                                      tf.MultiLine,
+                                                      tf);
+            Assert.Equal (list.Count, resultLines.Count ());
+            Assert.Equal (resultLines, list);
+            var formattedText = string.Empty;
+
+            foreach (string txt in list)
+            {
+                formattedText += txt;
+            }
+
+            Assert.Equal (expectedText, formattedText);
+        }
+    }
+
     public static IEnumerable<object []> FormatEnvironmentNewLine =>
         new List<object []>
         {