diff --git a/Terminal.Gui/Views/CharMap/CharMap.cs b/Terminal.Gui/Views/CharMap/CharMap.cs
index 03439e3529..2fb7a2d874 100644
--- a/Terminal.Gui/Views/CharMap/CharMap.cs
+++ b/Terminal.Gui/Views/CharMap/CharMap.cs
@@ -822,6 +822,22 @@ void RenderGrapheme (string grapheme, int width, int scalar)
break;
+ // Modifier symbol (e.g. emoji skin tone modifiers U+1F3FB-U+1F3FF) that reports
+ // width of 0 because it is designed to combine with a preceding base character.
+ case UnicodeCategory.ModifierSymbol:
+ if (width > 0)
+ {
+ AddStr (grapheme);
+ }
+ else
+ {
+ SetAttributeForRole (VisualRole.Highlight);
+ AddStr ("M");
+ SetAttributeForRole (VisualRole.Normal);
+ }
+
+ break;
+
case UnicodeCategory.OtherLetter:
AddStr (grapheme);
@@ -841,7 +857,11 @@ void RenderGrapheme (string grapheme, int width, int scalar)
}
else
{
- throw new InvalidOperationException ($"The Rune \"{grapheme}\" (U+{Rune.GetRuneAt (grapheme, 0).Value:x6}) has zero width and no special-case UnicodeCategory logic applies.");
+ // Fallback for any zero-width rune whose UnicodeCategory is not explicitly handled above.
+ // Display a highlighted placeholder to avoid crashes for future Unicode categories.
+ SetAttributeForRole (VisualRole.Highlight);
+ AddStr ("?");
+ SetAttributeForRole (VisualRole.Normal);
}
break;
diff --git a/Tests/UnitTestsParallelizable/Views/CharMapTests.cs b/Tests/UnitTestsParallelizable/Views/CharMapTests.cs
index 990d276501..223f176186 100644
--- a/Tests/UnitTestsParallelizable/Views/CharMapTests.cs
+++ b/Tests/UnitTestsParallelizable/Views/CharMapTests.cs
@@ -1,12 +1,13 @@
using System.Diagnostics.CodeAnalysis;
using System.Text;
+using UnitTests;
namespace ViewsTests;
///
/// Tests for command handling and IValue implementation.
///
-public class CharMapTests
+public class CharMapTests : TestDriverBase
{
///
/// Verifies that is raised when changes.
@@ -129,4 +130,43 @@ public void Value_Property_Change_Raises_ValueChangedUntyped ()
charMap.Dispose ();
}
+
+ ///
+ /// Verifies that drawing a with a ModifierSymbol code point (e.g. U+1F3FB, emoji skin tone
+ /// modifier) does not throw .
+ ///
+ [Fact]
+ [RequiresUnreferencedCode ("AOT")]
+ [RequiresDynamicCode ("AOT")]
+ public void Draw_ModifierSymbol_CodePoint_Does_Not_Throw ()
+ {
+ // U+1F3FB is EMOJI MODIFIER FITZPATRICK TYPE-1-2, classified as UnicodeCategory.ModifierSymbol
+ // with a column width of 0 (designed to combine with a preceding base emoji).
+ const int modifierSymbolCodePoint = 0x1F3FB;
+
+ IDriver driver = CreateTestDriver (80, 25);
+ driver.Clip = new Region (driver.Screen);
+
+ CharMap charMap = new ()
+ {
+ Driver = driver,
+ X = 0,
+ Y = 0,
+ Width = 80,
+ Height = 25,
+ StartCodePoint = modifierSymbolCodePoint & ~0xF // row containing U+1F3FB
+ };
+
+ charMap.SelectedCodePoint = modifierSymbolCodePoint;
+
+ charMap.BeginInit ();
+ charMap.EndInit ();
+ charMap.LayoutSubViews ();
+
+ // This must not throw InvalidOperationException
+ Exception? exception = Record.Exception (() => charMap.Draw ());
+ Assert.Null (exception);
+
+ charMap.Dispose ();
+ }
}