diff --git a/Avalonia.sln b/Avalonia.sln index 3a2c619d5b..f6dc039c2f 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -201,9 +201,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Dialogs", "src\Ava EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.FreeDesktop", "src\Avalonia.FreeDesktop\Avalonia.FreeDesktop.csproj", "{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Controls.DataGrid.UnitTests", "tests\Avalonia.Controls.DataGrid.UnitTests\Avalonia.Controls.DataGrid.UnitTests.csproj", "{351337F5-D66F-461B-A957-4EF60BDB4BA6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.DataGrid.UnitTests", "tests\Avalonia.Controls.DataGrid.UnitTests\Avalonia.Controls.DataGrid.UnitTests.csproj", "{351337F5-D66F-461B-A957-4EF60BDB4BA6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NativeEmbedSample", "samples\interop\NativeEmbedSample\NativeEmbedSample.csproj", "{3C84E04B-36CF-4D0D-B965-C26DD649D1F3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NativeEmbedSample", "samples\interop\NativeEmbedSample\NativeEmbedSample.csproj", "{3C84E04B-36CF-4D0D-B965-C26DD649D1F3}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Themes.Fluent", "src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj", "{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}" EndProject @@ -211,8 +211,8 @@ Global GlobalSection(SharedMSBuildProjectFiles) = preSolution src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13 src\Shared\RenderHelpers\RenderHelpers.projitems*{3e908f67-5543-4879-a1dc-08eace79b3cd}*SharedItemsImports = 5 - src\Shared\PlatformSupport\PlatformSupport.projitems*{4488ad85-1495-4809-9aa4-ddfe0a48527e}*SharedItemsImports = 4 - src\Shared\PlatformSupport\PlatformSupport.projitems*{7b92af71-6287-4693-9dcb-bd5b6e927e23}*SharedItemsImports = 4 + src\Shared\PlatformSupport\PlatformSupport.projitems*{4488ad85-1495-4809-9aa4-ddfe0a48527e}*SharedItemsImports = 5 + src\Shared\PlatformSupport\PlatformSupport.projitems*{7b92af71-6287-4693-9dcb-bd5b6e927e23}*SharedItemsImports = 5 src\Shared\RenderHelpers\RenderHelpers.projitems*{7d2d3083-71dd-4cc9-8907-39a0d86fb322}*SharedItemsImports = 5 src\Shared\PlatformSupport\PlatformSupport.projitems*{88060192-33d5-4932-b0f9-8bd2763e857d}*SharedItemsImports = 5 src\Shared\PlatformSupport\PlatformSupport.projitems*{e4d9629c-f168-4224-3f51-a5e482ffbc42}*SharedItemsImports = 13 diff --git a/src/Avalonia.Layout/LayoutManager.cs b/src/Avalonia.Layout/LayoutManager.cs index 792de774d1..fc988a8d6c 100644 --- a/src/Avalonia.Layout/LayoutManager.cs +++ b/src/Avalonia.Layout/LayoutManager.cs @@ -106,8 +106,6 @@ namespace Avalonia.Layout if (!_running) { - _running = true; - Stopwatch? stopwatch = null; const LogEventLevel timingLogLevel = LogEventLevel.Information; @@ -128,15 +126,24 @@ namespace Avalonia.Layout _toMeasure.BeginLoop(MaxPasses); _toArrange.BeginLoop(MaxPasses); - for (var pass = 0; pass < MaxPasses; ++pass) + try { - InnerLayoutPass(); + _running = true; - if (!RaiseEffectiveViewportChanged()) + for (var pass = 0; pass < MaxPasses; ++pass) { - break; + InnerLayoutPass(); + + if (!RaiseEffectiveViewportChanged()) + { + break; + } } } + finally + { + _running = false; + } _toMeasure.EndLoop(); _toArrange.EndLoop(); @@ -221,23 +228,16 @@ namespace Avalonia.Layout private void InnerLayoutPass() { - try + for (var pass = 0; pass < MaxPasses; ++pass) { - for (var pass = 0; pass < MaxPasses; ++pass) - { - ExecuteMeasurePass(); - ExecuteArrangePass(); + ExecuteMeasurePass(); + ExecuteArrangePass(); - if (_toMeasure.Count == 0) - { - break; - } + if (_toMeasure.Count == 0) + { + break; } } - finally - { - _running = false; - } } private void ExecuteMeasurePass() @@ -362,7 +362,7 @@ namespace Avalonia.Layout } } - return startCount != _toMeasure.Count + _toMeasure.Count; + return startCount != _toMeasure.Count + _toArrange.Count; } private Rect CalculateEffectiveViewport(IVisual control) diff --git a/src/Avalonia.Themes.Fluent/Accents/FluentBaseDark.xaml b/src/Avalonia.Themes.Fluent/Accents/FluentBaseDark.xaml index b12a639d31..b5d502787d 100644 --- a/src/Avalonia.Themes.Fluent/Accents/FluentBaseDark.xaml +++ b/src/Avalonia.Themes.Fluent/Accents/FluentBaseDark.xaml @@ -2,35 +2,8 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=netstandard"> - #FF0078D7 - #FF000000 - #FF000000 - #FF000000 - #FF000000 - #FF000000 - #FFFFFFFF - #FF333333 - #FF9A9A9A - #FFB4B4B4 - #FF676767 - #FFB4B4B4 - #FF000000 - #FFB4B4B4 - #FF000000 - #FF000000 - #FF333333 - #FF808080 - #FF808080 - #FF151515 - #FF1D1D1D - #FF2C2C2C - #FFFFFFFF - #FF1D1D1D - #FF333333 - #CC000000 - #FF333333 - #FF1D1D1D - #FF333333 + #18FFFFFF + #30FFFFFF @@ -67,13 +40,6 @@ 1,1,1,1 1 - - #FF005A9E - #FF004275 - #FF002642 - #FF429CE3 - #FF76B9ED - #FFA6D8FF #FF000000 diff --git a/src/Avalonia.Themes.Fluent/Accents/FluentBaseLight.xaml b/src/Avalonia.Themes.Fluent/Accents/FluentBaseLight.xaml index 31c2f592b9..0806a6e9ef 100644 --- a/src/Avalonia.Themes.Fluent/Accents/FluentBaseLight.xaml +++ b/src/Avalonia.Themes.Fluent/Accents/FluentBaseLight.xaml @@ -2,36 +2,8 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=netstandard"> - #FF0078D7 - #FFFFFFFF - #FFFFFFFF - #FFFFFFFF - #FFFFFFFF - #FFFFFFFF - #FF000000 - #FFCCCCCC - #FF898989 - #FF5D5D5D - #FF737373 - #FF5D5D5D - #FF000000 - #FFCCCCCC - #FF5D5D5D - #FF898989 - #FFCCCCCC - #FF898989 - #FF737373 - #FFCCCCCC - #FFECECEC - #FFE6E6E6 - #FFECECEC - #FFFFFFFF - #FFE6E6E6 - #FFCCCCCC - #CCFFFFFF - #FFCCCCCC - #FFE6E6E6 - #FFCCCCCC + #17000000 + #2E000000 @@ -68,13 +40,6 @@ 1,1,1,1 1 - - #FF005A9E - #FF004275 - #FF002642 - #FF429CE3 - #FF76B9ED - #FFA6D8FF #FFFFFFFF diff --git a/src/Avalonia.Visuals/Assets/GraphemeBreak.trie b/src/Avalonia.Visuals/Assets/GraphemeBreak.trie index 704dea4e86..482bf9b44d 100644 Binary files a/src/Avalonia.Visuals/Assets/GraphemeBreak.trie and b/src/Avalonia.Visuals/Assets/GraphemeBreak.trie differ diff --git a/src/Avalonia.Visuals/Assets/UnicodeData.trie b/src/Avalonia.Visuals/Assets/UnicodeData.trie index 2e39745646..f96106a5fa 100644 Binary files a/src/Avalonia.Visuals/Assets/UnicodeData.trie and b/src/Avalonia.Visuals/Assets/UnicodeData.trie differ diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/BiDiClass.cs b/src/Avalonia.Visuals/Media/TextFormatting/Unicode/BiDiClass.cs index 03576a4c40..ad3cc9141b 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/BiDiClass.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/Unicode/BiDiClass.cs @@ -2,6 +2,7 @@ namespace Avalonia.Media.TextFormatting.Unicode { public enum BiDiClass { + LeftToRight, //L ArabicLetter, //AL ArabicNumber, //AN ParagraphSeparator, //B @@ -11,7 +12,6 @@ namespace Avalonia.Media.TextFormatting.Unicode EuropeanSeparator, //ES EuropeanTerminator, //ET FirstStrongIsolate, //FSI - LeftToRight, //L LeftToRightEmbedding, //LRE LeftToRightIsolate, //LRI LeftToRightOverride, //LRO diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/BreakPairTable.cs b/src/Avalonia.Visuals/Media/TextFormatting/Unicode/BreakPairTable.cs index c13074711e..86d39a4283 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/BreakPairTable.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/Unicode/BreakPairTable.cs @@ -4,38 +4,39 @@ namespace Avalonia.Media.TextFormatting.Unicode { private static readonly byte[][] s_breakPairTable = { - new byte[] {4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,4,4,4,4,4,4,4,4,4,4}, - new byte[] {0,4,4,1,1,4,4,4,4,1,1,0,0,0,0,0,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1}, - new byte[] {0,4,4,1,1,4,4,4,4,1,1,1,1,1,0,0,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1}, - new byte[] {4,4,4,1,1,1,4,4,4,1,1,1,1,1,1,1,1,1,1,1,4,2,4,1,1,1,1,1,1,1,1,1}, - new byte[] {1,4,4,1,1,1,4,4,4,1,1,1,1,1,1,1,1,1,1,1,4,2,4,1,1,1,1,1,1,1,1,1}, - new byte[] {0,4,4,1,1,1,4,4,4,0,0,0,0,0,0,0,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1}, - new byte[] {0,4,4,1,1,1,4,4,4,0,0,0,0,0,0,1,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1}, - new byte[] {0,4,4,1,1,1,4,4,4,0,0,1,0,1,0,0,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1}, - new byte[] {0,4,4,1,1,1,4,4,4,0,0,1,1,1,0,0,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1}, - new byte[] {1,4,4,1,1,1,4,4,4,0,0,1,1,1,1,0,1,1,0,0,4,2,4,1,1,1,1,1,0,1,1,1}, - new byte[] {1,4,4,1,1,1,4,4,4,0,0,1,1,1,0,0,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1}, - new byte[] {1,4,4,1,1,1,4,4,4,1,1,1,1,1,0,1,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1}, - new byte[] {1,4,4,1,1,1,4,4,4,1,1,1,1,1,0,1,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1}, - new byte[] {1,4,4,1,1,1,4,4,4,1,1,1,1,1,0,1,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1}, - new byte[] {0,4,4,1,1,1,4,4,4,0,1,0,0,0,0,1,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1}, - new byte[] {0,4,4,1,1,1,4,4,4,0,0,0,0,0,0,1,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1}, - new byte[] {0,4,4,1,0,1,4,4,4,0,0,1,0,0,0,0,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1}, - new byte[] {0,4,4,1,0,1,4,4,4,0,0,0,0,0,0,0,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1}, - new byte[] {1,4,4,1,1,1,4,4,4,1,1,1,1,1,1,1,1,1,1,1,4,2,4,1,1,1,1,1,1,1,1,1}, - new byte[] {0,4,4,1,1,1,4,4,4,0,0,0,0,0,0,0,1,1,0,4,4,2,4,0,0,0,0,0,0,0,0,1}, - new byte[] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0}, - new byte[] {1,4,4,1,1,1,4,4,4,1,1,1,1,1,0,1,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1}, - new byte[] {1,4,4,1,1,1,4,4,4,1,1,1,1,1,1,1,1,1,1,1,4,2,4,1,1,1,1,1,1,1,1,1}, - new byte[] {0,4,4,1,1,1,4,4,4,0,1,0,0,0,0,1,1,1,0,0,4,2,4,0,0,0,1,1,0,0,0,1}, - new byte[] {0,4,4,1,1,1,4,4,4,0,1,0,0,0,0,1,1,1,0,0,4,2,4,0,0,0,0,1,0,0,0,1}, - new byte[] {0,4,4,1,1,1,4,4,4,0,1,0,0,0,0,1,1,1,0,0,4,2,4,1,1,1,1,0,0,0,0,1}, - new byte[] {0,4,4,1,1,1,4,4,4,0,1,0,0,0,0,1,1,1,0,0,4,2,4,0,0,0,1,1,0,0,0,1}, - new byte[] {0,4,4,1,1,1,4,4,4,0,1,0,0,0,0,1,1,1,0,0,4,2,4,0,0,0,0,1,0,0,0,1}, - new byte[] {0,4,4,1,1,1,4,4,4,0,0,0,0,0,0,0,1,1,0,0,4,2,4,0,0,0,0,0,1,0,0,1}, - new byte[] {0,4,4,1,1,1,4,4,4,0,1,0,0,0,0,1,1,1,0,0,4,2,4,0,0,0,0,0,0,0,1,1}, - new byte[] {0,4,4,1,1,1,4,4,4,0,1,0,0,0,0,1,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1}, - new byte[] {0,4,4,1,1,1,4,4,4,0,0,0,0,0,1,0,1,1,0,0,4,2,4,0,0,0,0,0,0,1,1,1}, + new byte[] {4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,4,4,4,4,4,4,4,4,4,4,4}, + new byte[] {0,4,4,1,1,4,4,4,4,1,1,0,0,0,0,4,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1,0}, + new byte[] {0,4,4,1,1,4,4,4,4,1,1,1,1,1,0,4,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1,0}, + new byte[] {4,4,4,1,1,1,4,4,4,1,1,1,1,1,1,1,1,1,1,1,4,2,4,1,1,1,1,1,1,1,1,1,1}, + new byte[] {1,4,4,1,1,1,4,4,4,1,1,1,1,1,1,1,1,1,1,1,4,2,4,1,1,1,1,1,1,1,1,1,1}, + new byte[] {0,4,4,1,1,1,4,4,4,0,0,0,0,0,0,0,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1,0}, + new byte[] {0,4,4,1,1,1,4,4,4,0,0,0,0,0,0,1,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1,0}, + new byte[] {0,4,4,1,1,1,4,4,4,0,0,1,0,1,0,0,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1,0}, + new byte[] {0,4,4,1,1,1,4,4,4,0,0,1,1,1,0,0,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1,0}, + new byte[] {1,4,4,1,1,1,4,4,4,0,0,1,1,1,1,0,1,1,0,0,4,2,4,1,1,1,1,1,0,1,1,1,0}, + new byte[] {1,4,4,1,1,1,4,4,4,0,0,1,1,1,0,0,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1,0}, + new byte[] {1,4,4,1,1,1,4,4,4,1,1,1,1,1,0,1,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1,0}, + new byte[] {1,4,4,1,1,1,4,4,4,1,1,1,1,1,0,1,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1,0}, + new byte[] {1,4,4,1,1,1,4,4,4,1,1,1,1,1,0,1,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1,0}, + new byte[] {0,4,4,1,1,1,4,4,4,0,1,0,0,0,0,1,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1,0}, + new byte[] {0,4,4,1,1,1,4,4,4,0,0,0,0,0,0,1,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1,0}, + new byte[] {0,4,4,1,0,1,4,4,4,0,0,1,0,0,0,0,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1,0}, + new byte[] {0,4,4,1,0,1,4,4,4,0,0,0,0,0,0,0,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1,0}, + new byte[] {1,4,4,1,1,1,4,4,4,1,1,1,1,1,1,1,1,1,1,1,4,2,4,1,1,1,1,1,1,1,1,1,0}, + new byte[] {0,4,4,1,1,1,4,4,4,0,0,0,0,0,0,0,1,1,0,4,4,2,4,0,0,0,0,0,0,0,0,1,0}, + new byte[] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0}, + new byte[] {1,4,4,1,1,1,4,4,4,1,1,1,1,1,0,1,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1,0}, + new byte[] {1,4,4,1,1,1,4,4,4,1,1,1,1,1,1,1,1,1,1,1,4,2,4,1,1,1,1,1,1,1,1,1,1}, + new byte[] {0,4,4,1,1,1,4,4,4,0,1,0,0,0,0,1,1,1,0,0,4,2,4,0,0,0,1,1,0,0,0,1,0}, + new byte[] {0,4,4,1,1,1,4,4,4,0,1,0,0,0,0,1,1,1,0,0,4,2,4,0,0,0,0,1,0,0,0,1,0}, + new byte[] {0,4,4,1,1,1,4,4,4,0,1,0,0,0,0,1,1,1,0,0,4,2,4,1,1,1,1,0,0,0,0,1,0}, + new byte[] {0,4,4,1,1,1,4,4,4,0,1,0,0,0,0,1,1,1,0,0,4,2,4,0,0,0,1,1,0,0,0,1,0}, + new byte[] {0,4,4,1,1,1,4,4,4,0,1,0,0,0,0,1,1,1,0,0,4,2,4,0,0,0,0,1,0,0,0,1,0}, + new byte[] {0,4,4,1,1,1,4,4,4,0,0,0,0,0,0,0,1,1,0,0,4,2,4,0,0,0,0,0,1,0,0,1,0}, + new byte[] {0,4,4,1,1,1,4,4,4,0,1,0,0,0,0,1,1,1,0,0,4,2,4,0,0,0,0,0,0,0,1,1,0}, + new byte[] {0,4,4,1,1,1,4,4,4,0,1,0,0,0,0,1,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1,0}, + new byte[] {1,4,4,1,1,1,4,4,4,1,1,1,1,1,0,1,1,1,0,0,4,2,4,0,0,0,0,0,0,0,0,1,0}, + new byte[] {0,4,4,1,1,0,4,4,4,0,0,0,0,0,0,0,0,0,0,0,4,2,4,0,0,0,0,0,0,0,0,1,0}, }; public static PairBreakType Map(LineBreakClass first, LineBreakClass second) diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/GraphemeBreakClass.cs b/src/Avalonia.Visuals/Media/TextFormatting/Unicode/GraphemeBreakClass.cs index 684baae51f..71e4bce106 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/GraphemeBreakClass.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/Unicode/GraphemeBreakClass.cs @@ -2,24 +2,20 @@ namespace Avalonia.Media.TextFormatting.Unicode { public enum GraphemeBreakClass { - Control, //CN - CR, //CR - EBase, //EB - EBaseGAZ, //EBG - EModifier, //EM - Extend, //EX - GlueAfterZwj, //GAZ - L, //L - LF, //LF - LV, //LV - LVT, //LVT - Prepend, //PP - RegionalIndicator, //RI - SpacingMark, //SM - T, //T - V, //V - Other, //XX - ZWJ, //ZWJ - ExtendedPictographic + Other, + CR, + LF, + Control, + Extend, + ZWJ, + RegionalIndicator, + Prepend, + SpacingMark, + L, + V, + T, + LV, + LVT, + ExtendedPictographic, } } diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreakClass.cs b/src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreakClass.cs index 925706dd4f..8b2e3f41e3 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreakClass.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreakClass.cs @@ -34,10 +34,11 @@ namespace Avalonia.Media.TextFormatting.Unicode EBase, //EB EModifier, //EM ZWJ, //ZWJ + ContingentBreak, //CB + Unknown, //XX Ambiguous, //AI MandatoryBreak, //BK - ContingentBreak, //CB ConditionalJapaneseStarter, //CJ CarriageReturn, //CR LineFeed, //LF @@ -45,6 +46,5 @@ namespace Avalonia.Media.TextFormatting.Unicode ComplexContext, //SA Surrogate, //SG Space, //SP - Unknown, //XX } } diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreakEnumerator.cs b/src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreakEnumerator.cs index a11c008409..25a32bb1a3 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreakEnumerator.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreakEnumerator.cs @@ -95,7 +95,8 @@ namespace Avalonia.Media.TextFormatting.Unicode if (_nextClass.Value == LineBreakClass.MandatoryBreak) { - Current = new LineBreak(FindPriorNonWhitespace(_lastPos), _lastPos); + _lastPos = _pos; + Current = new LineBreak(FindPriorNonWhitespace(_lastPos), _lastPos, true); return true; } @@ -108,6 +109,7 @@ namespace Avalonia.Media.TextFormatting.Unicode { case PairBreakType.DI: // Direct break shouldBreak = true; + _lastPos = _pos; break; case PairBreakType.IN: // possible indirect break diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/PropertyValueAliasHelper.cs b/src/Avalonia.Visuals/Media/TextFormatting/Unicode/PropertyValueAliasHelper.cs new file mode 100644 index 0000000000..388a7d257d --- /dev/null +++ b/src/Avalonia.Visuals/Media/TextFormatting/Unicode/PropertyValueAliasHelper.cs @@ -0,0 +1,178 @@ +using System.Collections.Generic; + +namespace Avalonia.Media.TextFormatting.Unicode +{ + internal static class PropertyValueAliasHelper + { + private static readonly Dictionary s_scriptToTag = + new Dictionary{ + { Script.Unknown, "Zzzz"}, + { Script.Common, "Zyyy"}, + { Script.Inherited, "Zinh"}, + { Script.Adlam, "Adlm"}, + { Script.CaucasianAlbanian, "Aghb"}, + { Script.Ahom, "Ahom"}, + { Script.Arabic, "Arab"}, + { Script.ImperialAramaic, "Armi"}, + { Script.Armenian, "Armn"}, + { Script.Avestan, "Avst"}, + { Script.Balinese, "Bali"}, + { Script.Bamum, "Bamu"}, + { Script.BassaVah, "Bass"}, + { Script.Batak, "Batk"}, + { Script.Bengali, "Beng"}, + { Script.Bhaiksuki, "Bhks"}, + { Script.Bopomofo, "Bopo"}, + { Script.Brahmi, "Brah"}, + { Script.Braille, "Brai"}, + { Script.Buginese, "Bugi"}, + { Script.Buhid, "Buhd"}, + { Script.Chakma, "Cakm"}, + { Script.CanadianAboriginal, "Cans"}, + { Script.Carian, "Cari"}, + { Script.Cham, "Cham"}, + { Script.Cherokee, "Cher"}, + { Script.Chorasmian, "Chrs"}, + { Script.Coptic, "Copt"}, + { Script.Cypriot, "Cprt"}, + { Script.Cyrillic, "Cyrl"}, + { Script.Devanagari, "Deva"}, + { Script.DivesAkuru, "Diak"}, + { Script.Dogra, "Dogr"}, + { Script.Deseret, "Dsrt"}, + { Script.Duployan, "Dupl"}, + { Script.EgyptianHieroglyphs, "Egyp"}, + { Script.Elbasan, "Elba"}, + { Script.Elymaic, "Elym"}, + { Script.Ethiopic, "Ethi"}, + { Script.Georgian, "Geor"}, + { Script.Glagolitic, "Glag"}, + { Script.GunjalaGondi, "Gong"}, + { Script.MasaramGondi, "Gonm"}, + { Script.Gothic, "Goth"}, + { Script.Grantha, "Gran"}, + { Script.Greek, "Grek"}, + { Script.Gujarati, "Gujr"}, + { Script.Gurmukhi, "Guru"}, + { Script.Hangul, "Hang"}, + { Script.Han, "Hani"}, + { Script.Hanunoo, "Hano"}, + { Script.Hatran, "Hatr"}, + { Script.Hebrew, "Hebr"}, + { Script.Hiragana, "Hira"}, + { Script.AnatolianHieroglyphs, "Hluw"}, + { Script.PahawhHmong, "Hmng"}, + { Script.NyiakengPuachueHmong, "Hmnp"}, + { Script.KatakanaOrHiragana, "Hrkt"}, + { Script.OldHungarian, "Hung"}, + { Script.OldItalic, "Ital"}, + { Script.Javanese, "Java"}, + { Script.KayahLi, "Kali"}, + { Script.Katakana, "Kana"}, + { Script.Kharoshthi, "Khar"}, + { Script.Khmer, "Khmr"}, + { Script.Khojki, "Khoj"}, + { Script.KhitanSmallScript, "Kits"}, + { Script.Kannada, "Knda"}, + { Script.Kaithi, "Kthi"}, + { Script.TaiTham, "Lana"}, + { Script.Lao, "Laoo"}, + { Script.Latin, "Latn"}, + { Script.Lepcha, "Lepc"}, + { Script.Limbu, "Limb"}, + { Script.LinearA, "Lina"}, + { Script.LinearB, "Linb"}, + { Script.Lisu, "Lisu"}, + { Script.Lycian, "Lyci"}, + { Script.Lydian, "Lydi"}, + { Script.Mahajani, "Mahj"}, + { Script.Makasar, "Maka"}, + { Script.Mandaic, "Mand"}, + { Script.Manichaean, "Mani"}, + { Script.Marchen, "Marc"}, + { Script.Medefaidrin, "Medf"}, + { Script.MendeKikakui, "Mend"}, + { Script.MeroiticCursive, "Merc"}, + { Script.MeroiticHieroglyphs, "Mero"}, + { Script.Malayalam, "Mlym"}, + { Script.Modi, "Modi"}, + { Script.Mongolian, "Mong"}, + { Script.Mro, "Mroo"}, + { Script.MeeteiMayek, "Mtei"}, + { Script.Multani, "Mult"}, + { Script.Myanmar, "Mymr"}, + { Script.Nandinagari, "Nand"}, + { Script.OldNorthArabian, "Narb"}, + { Script.Nabataean, "Nbat"}, + { Script.Newa, "Newa"}, + { Script.Nko, "Nkoo"}, + { Script.Nushu, "Nshu"}, + { Script.Ogham, "Ogam"}, + { Script.OlChiki, "Olck"}, + { Script.OldTurkic, "Orkh"}, + { Script.Oriya, "Orya"}, + { Script.Osage, "Osge"}, + { Script.Osmanya, "Osma"}, + { Script.Palmyrene, "Palm"}, + { Script.PauCinHau, "Pauc"}, + { Script.OldPermic, "Perm"}, + { Script.PhagsPa, "Phag"}, + { Script.InscriptionalPahlavi, "Phli"}, + { Script.PsalterPahlavi, "Phlp"}, + { Script.Phoenician, "Phnx"}, + { Script.Miao, "Plrd"}, + { Script.InscriptionalParthian, "Prti"}, + { Script.Rejang, "Rjng"}, + { Script.HanifiRohingya, "Rohg"}, + { Script.Runic, "Runr"}, + { Script.Samaritan, "Samr"}, + { Script.OldSouthArabian, "Sarb"}, + { Script.Saurashtra, "Saur"}, + { Script.SignWriting, "Sgnw"}, + { Script.Shavian, "Shaw"}, + { Script.Sharada, "Shrd"}, + { Script.Siddham, "Sidd"}, + { Script.Khudawadi, "Sind"}, + { Script.Sinhala, "Sinh"}, + { Script.Sogdian, "Sogd"}, + { Script.OldSogdian, "Sogo"}, + { Script.SoraSompeng, "Sora"}, + { Script.Soyombo, "Soyo"}, + { Script.Sundanese, "Sund"}, + { Script.SylotiNagri, "Sylo"}, + { Script.Syriac, "Syrc"}, + { Script.Tagbanwa, "Tagb"}, + { Script.Takri, "Takr"}, + { Script.TaiLe, "Tale"}, + { Script.NewTaiLue, "Talu"}, + { Script.Tamil, "Taml"}, + { Script.Tangut, "Tang"}, + { Script.TaiViet, "Tavt"}, + { Script.Telugu, "Telu"}, + { Script.Tifinagh, "Tfng"}, + { Script.Tagalog, "Tglg"}, + { Script.Thaana, "Thaa"}, + { Script.Thai, "Thai"}, + { Script.Tibetan, "Tibt"}, + { Script.Tirhuta, "Tirh"}, + { Script.Ugaritic, "Ugar"}, + { Script.Vai, "Vaii"}, + { Script.WarangCiti, "Wara"}, + { Script.Wancho, "Wcho"}, + { Script.OldPersian, "Xpeo"}, + { Script.Cuneiform, "Xsux"}, + { Script.Yezidi, "Yezi"}, + { Script.Yi, "Yiii"}, + { Script.ZanabazarSquare, "Zanb"}, + }; + + public static string GetTag(Script script) + { + if(!s_scriptToTag.ContainsKey(script)) + { + return "Zzzz"; + } + return s_scriptToTag[script]; + } + } +} diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/Script.cs b/src/Avalonia.Visuals/Media/TextFormatting/Unicode/Script.cs index e9681d4c24..2593a77848 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/Script.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/Unicode/Script.cs @@ -2,6 +2,9 @@ namespace Avalonia.Media.TextFormatting.Unicode { public enum Script { + Unknown, //Zzzz + Common, //Zyyy + Inherited, //Zinh Adlam, //Adlm CaucasianAlbanian, //Aghb Ahom, //Ahom @@ -25,10 +28,12 @@ namespace Avalonia.Media.TextFormatting.Unicode Carian, //Cari Cham, //Cham Cherokee, //Cher + Chorasmian, //Chrs Coptic, //Copt Cypriot, //Cprt Cyrillic, //Cyrl Devanagari, //Deva + DivesAkuru, //Diak Dogra, //Dogr Deseret, //Dsrt Duployan, //Dupl @@ -63,6 +68,7 @@ namespace Avalonia.Media.TextFormatting.Unicode Kharoshthi, //Khar Khmer, //Khmr Khojki, //Khoj + KhitanSmallScript, //Kits Kannada, //Knda Kaithi, //Kthi TaiTham, //Lana @@ -151,10 +157,8 @@ namespace Avalonia.Media.TextFormatting.Unicode Wancho, //Wcho OldPersian, //Xpeo Cuneiform, //Xsux + Yezidi, //Yezi Yi, //Yiii ZanabazarSquare, //Zanb - Inherited, //Zinh - Common, //Zyyy - Unknown, //Zzzz } } diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/BreakPairTable.txt b/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/BreakPairTable.txt index 90c1e2cee1..814ce15d0a 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/BreakPairTable.txt +++ b/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/BreakPairTable.txt @@ -1,33 +1,34 @@ - OP CL CP QU GL NS EX SY IS PR PO NU AL HL ID IN HY BA BB B2 ZW CM WJ H2 H3 JL JV JT RI EB EM ZWJ -OP ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ @ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ -CL _ ^ ^ % % ^ ^ ^ ^ % % _ _ _ _ _ % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % -CP _ ^ ^ % % ^ ^ ^ ^ % % % % % _ _ % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % -QU ^ ^ ^ % % % ^ ^ ^ % % % % % % % % % % % ^ # ^ % % % % % % % % % -GL % ^ ^ % % % ^ ^ ^ % % % % % % % % % % % ^ # ^ % % % % % % % % % -NS _ ^ ^ % % % ^ ^ ^ _ _ _ _ _ _ _ % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % -EX _ ^ ^ % % % ^ ^ ^ _ _ _ _ _ _ % % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % -SY _ ^ ^ % % % ^ ^ ^ _ _ % _ % _ _ % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % -IS _ ^ ^ % % % ^ ^ ^ _ _ % % % _ _ % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % -PR % ^ ^ % % % ^ ^ ^ _ _ % % % % _ % % _ _ ^ # ^ % % % % % _ % % % -PO % ^ ^ % % % ^ ^ ^ _ _ % % % _ _ % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % -NU % ^ ^ % % % ^ ^ ^ % % % % % _ % % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % -AL % ^ ^ % % % ^ ^ ^ % % % % % _ % % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % -HL % ^ ^ % % % ^ ^ ^ % % % % % _ % % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % -ID _ ^ ^ % % % ^ ^ ^ _ % _ _ _ _ % % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % -IN _ ^ ^ % % % ^ ^ ^ _ _ _ _ _ _ % % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % -HY _ ^ ^ % _ % ^ ^ ^ _ _ % _ _ _ _ % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % -BA _ ^ ^ % _ % ^ ^ ^ _ _ _ _ _ _ _ % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % -BB % ^ ^ % % % ^ ^ ^ % % % % % % % % % % % ^ # ^ % % % % % % % % % -B2 _ ^ ^ % % % ^ ^ ^ _ _ _ _ _ _ _ % % _ ^ ^ # ^ _ _ _ _ _ _ _ _ % -ZW _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ^ _ _ _ _ _ _ _ _ _ _ _ -CM % ^ ^ % % % ^ ^ ^ % % % % % _ % % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % -WJ % ^ ^ % % % ^ ^ ^ % % % % % % % % % % % ^ # ^ % % % % % % % % % -H2 _ ^ ^ % % % ^ ^ ^ _ % _ _ _ _ % % % _ _ ^ # ^ _ _ _ % % _ _ _ % -H3 _ ^ ^ % % % ^ ^ ^ _ % _ _ _ _ % % % _ _ ^ # ^ _ _ _ _ % _ _ _ % -JL _ ^ ^ % % % ^ ^ ^ _ % _ _ _ _ % % % _ _ ^ # ^ % % % % _ _ _ _ % -JV _ ^ ^ % % % ^ ^ ^ _ % _ _ _ _ % % % _ _ ^ # ^ _ _ _ % % _ _ _ % -JT _ ^ ^ % % % ^ ^ ^ _ % _ _ _ _ % % % _ _ ^ # ^ _ _ _ _ % _ _ _ % -RI _ ^ ^ % % % ^ ^ ^ _ _ _ _ _ _ _ % % _ _ ^ # ^ _ _ _ _ _ % _ _ % -EB _ ^ ^ % % % ^ ^ ^ _ % _ _ _ _ % % % _ _ ^ # ^ _ _ _ _ _ _ _ % % -EM _ ^ ^ % % % ^ ^ ^ _ % _ _ _ _ % % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % -ZWJ _ ^ ^ % % % ^ ^ ^ _ _ _ _ _ % _ % % _ _ ^ # ^ _ _ _ _ _ _ % % % + OP CL CP QU GL NS EX SY IS PR PO NU AL HL ID IN HY BA BB B2 ZW CM WJ H2 H3 JL JV JT RI EB EM ZWJ CB +OP ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ @ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ +CL _ ^ ^ % % ^ ^ ^ ^ % % _ _ _ _ ^ % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % _ +CP _ ^ ^ % % ^ ^ ^ ^ % % % % % _ ^ % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % _ +QU ^ ^ ^ % % % ^ ^ ^ % % % % % % % % % % % ^ # ^ % % % % % % % % % % +GL % ^ ^ % % % ^ ^ ^ % % % % % % % % % % % ^ # ^ % % % % % % % % % % +NS _ ^ ^ % % % ^ ^ ^ _ _ _ _ _ _ _ % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % _ +EX _ ^ ^ % % % ^ ^ ^ _ _ _ _ _ _ % % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % _ +SY _ ^ ^ % % % ^ ^ ^ _ _ % _ % _ _ % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % _ +IS _ ^ ^ % % % ^ ^ ^ _ _ % % % _ _ % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % _ +PR % ^ ^ % % % ^ ^ ^ _ _ % % % % _ % % _ _ ^ # ^ % % % % % _ % % % _ +PO % ^ ^ % % % ^ ^ ^ _ _ % % % _ _ % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % _ +NU % ^ ^ % % % ^ ^ ^ % % % % % _ % % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % _ +AL % ^ ^ % % % ^ ^ ^ % % % % % _ % % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % _ +HL % ^ ^ % % % ^ ^ ^ % % % % % _ % % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % _ +ID _ ^ ^ % % % ^ ^ ^ _ % _ _ _ _ % % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % _ +IN _ ^ ^ % % % ^ ^ ^ _ _ _ _ _ _ % % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % _ +HY _ ^ ^ % _ % ^ ^ ^ _ _ % _ _ _ _ % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % _ +BA _ ^ ^ % _ % ^ ^ ^ _ _ _ _ _ _ _ % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % _ +BB % ^ ^ % % % ^ ^ ^ % % % % % % % % % % % ^ # ^ % % % % % % % % % _ +B2 _ ^ ^ % % % ^ ^ ^ _ _ _ _ _ _ _ % % _ ^ ^ # ^ _ _ _ _ _ _ _ _ % _ +ZW _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ^ _ _ _ _ _ _ _ _ _ _ _ _ +CM % ^ ^ % % % ^ ^ ^ % % % % % _ % % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % _ +WJ % ^ ^ % % % ^ ^ ^ % % % % % % % % % % % ^ # ^ % % % % % % % % % % +H2 _ ^ ^ % % % ^ ^ ^ _ % _ _ _ _ % % % _ _ ^ # ^ _ _ _ % % _ _ _ % _ +H3 _ ^ ^ % % % ^ ^ ^ _ % _ _ _ _ % % % _ _ ^ # ^ _ _ _ _ % _ _ _ % _ +JL _ ^ ^ % % % ^ ^ ^ _ % _ _ _ _ % % % _ _ ^ # ^ % % % % _ _ _ _ % _ +JV _ ^ ^ % % % ^ ^ ^ _ % _ _ _ _ % % % _ _ ^ # ^ _ _ _ % % _ _ _ % _ +JT _ ^ ^ % % % ^ ^ ^ _ % _ _ _ _ % % % _ _ ^ # ^ _ _ _ _ % _ _ _ % _ +RI _ ^ ^ % % % ^ ^ ^ _ _ _ _ _ _ _ % % _ _ ^ # ^ _ _ _ _ _ % _ _ % _ +EB _ ^ ^ % % % ^ ^ ^ _ % _ _ _ _ % % % _ _ ^ # ^ _ _ _ _ _ _ _ % % _ +EM _ ^ ^ % % % ^ ^ ^ _ % _ _ _ _ % % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % _ +ZWJ % ^ ^ % % % ^ ^ ^ % % % % % _ % % % _ _ ^ # ^ _ _ _ _ _ _ _ _ % _ +CB _ ^ ^ % % _ ^ ^ ^ _ _ _ _ _ _ _ _ _ _ _ ^ # ^ _ _ _ _ _ _ _ _ % _ diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGenerator.cs b/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGenerator.cs index 94ab615130..c074e5a1f1 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGenerator.cs +++ b/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGenerator.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Net.Http; using System.Text.RegularExpressions; using Avalonia.Media.TextFormatting.Unicode; @@ -12,6 +11,11 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting { public static void Execute() { + if (!Directory.Exists("Generated")) + { + Directory.CreateDirectory("Generated"); + } + using (var stream = File.Create("Generated\\GraphemeBreak.trie")) { var trie = GenerateBreakTypeTrie(); @@ -22,48 +26,29 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting private static UnicodeTrie GenerateBreakTypeTrie() { - var graphemeBreakClassValues = UnicodeEnumsGenerator.GetPropertyValueAliases("# Grapheme_Cluster_Break (GCB)"); - - var graphemeBreakClassMapping = graphemeBreakClassValues.Select(x => x.name).ToList(); - var trieBuilder = new UnicodeTrieBuilder(); - var graphemeBreakData = ReadBreakData( - "https://www.unicode.org/Public/UCD/latest/ucd/auxiliary/GraphemeBreakProperty.txt"); - - foreach (var (start, end, graphemeBreakType) in graphemeBreakData) - { - if (!graphemeBreakClassMapping.Contains(graphemeBreakType)) - { - continue; - } - - if (start == end) - { - trieBuilder.Set(start, (uint)graphemeBreakClassMapping.IndexOf(graphemeBreakType)); - } - else - { - trieBuilder.SetRange(start, end, (uint)graphemeBreakClassMapping.IndexOf(graphemeBreakType)); - } - } + var graphemeBreakData = ReadBreakData(Path.Combine(UnicodeDataGenerator.Ucd, "auxiliary/GraphemeBreakProperty.txt")); - var emojiBreakData = ReadBreakData("https://unicode.org/Public/emoji/12.0/emoji-data.txt"); + var emojiBreakData = ReadBreakData(Path.Combine(UnicodeDataGenerator.Ucd, "emoji/emoji-data.txt")); - foreach (var (start, end, graphemeBreakType) in emojiBreakData) + foreach (var breakData in new [] { graphemeBreakData, emojiBreakData }) { - if (!graphemeBreakClassMapping.Contains(graphemeBreakType)) + foreach (var (start, end, graphemeBreakType) in breakData) { - continue; - } + if (!Enum.TryParse(graphemeBreakType, out var value)) + { + continue; + } - if (start == end) - { - trieBuilder.Set(start, (uint)graphemeBreakClassMapping.IndexOf(graphemeBreakType)); - } - else - { - trieBuilder.SetRange(start, end, (uint)graphemeBreakClassMapping.IndexOf(graphemeBreakType)); + if (start == end) + { + trieBuilder.Set(start, (uint)value); + } + else + { + trieBuilder.SetRange(start, end, (uint)value); + } } } @@ -113,7 +98,9 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting end = Convert.ToInt32(match.Groups[2].Value, 16); } - data.Add((start, end, match.Groups[3].Value)); + var breakType = match.Groups[3].Value; + + data.Add((start, end, breakType)); } } } diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGeneratorTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGeneratorTests.cs index d9a9c82f85..848b60b341 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGeneratorTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGeneratorTests.cs @@ -1,9 +1,4 @@ using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net.Http; using Avalonia.Media.TextFormatting.Unicode; using Xunit; @@ -16,10 +11,12 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting public class GraphemeBreakClassTrieGeneratorTests { [Theory(Skip = "Only run when we update the trie.")] - [ClassData(typeof(GraphemeEnumeratorTestDataGenerator))] + [ClassData(typeof(GraphemeBreakTestDataGenerator))] public void Should_Enumerate(string text, int expectedLength) { - var enumerator = new GraphemeEnumerator(text.AsMemory()); + var textMemory = text.AsMemory(); + + var enumerator = new GraphemeEnumerator(textMemory); Assert.True(enumerator.MoveNext()); @@ -31,7 +28,9 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting { const string text = "ABCDEFGHIJ"; - var enumerator = new GraphemeEnumerator(text.AsMemory()); + var textMemory = text.AsMemory(); + + var enumerator = new GraphemeEnumerator(textMemory); var count = 0; @@ -51,73 +50,11 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting GraphemeBreakClassTrieGenerator.Execute(); } - public class GraphemeEnumeratorTestDataGenerator : IEnumerable + private class GraphemeBreakTestDataGenerator : TestDataGenerator { - private readonly List _testData; - - public GraphemeEnumeratorTestDataGenerator() - { - _testData = ReadTestData(); - } - - public IEnumerator GetEnumerator() - { - return _testData.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - private static List ReadTestData() + public GraphemeBreakTestDataGenerator() + : base("auxiliary/GraphemeBreakTest.txt") { - var testData = new List(); - - using (var client = new HttpClient()) - { - using (var result = client.GetAsync("https://www.unicode.org/Public/UNIDATA/auxiliary/GraphemeBreakTest.txt").GetAwaiter().GetResult()) - { - if (!result.IsSuccessStatusCode) - return testData; - - using (var stream = result.Content.ReadAsStreamAsync().GetAwaiter().GetResult()) - using (var reader = new StreamReader(stream)) - { - while (!reader.EndOfStream) - { - var line = reader.ReadLine(); - - if (line == null) - { - break; - } - - if (line.StartsWith("#") || string.IsNullOrEmpty(line)) - { - continue; - } - - var elements = line.Split('#')[0].Replace("÷\t", "÷").Trim('÷').Split('÷'); - - var chars = elements[0].Replace(" × ", " ").Split(' '); - - var codepoints = chars.Where(x => x != "" && x != "×") - .Select(x => Convert.ToInt32(x, 16)).ToArray(); - - var text = string.Join(null, codepoints.Select(char.ConvertFromUtf32)); - - var length = codepoints.Select(x => x > ushort.MaxValue ? 2 : 1).Sum(); - - var data = new object[] { text, length }; - - testData.Add(data); - } - } - } - } - - return testData; } } } diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/LineBreakerTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/LineBreakerTests.cs index fe7d7adc17..3ed5cfb0b2 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/LineBreakerTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/LineBreakerTests.cs @@ -3,7 +3,7 @@ using Avalonia.Media.TextFormatting.Unicode; using Avalonia.Utility; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media.Text +namespace Avalonia.Visuals.UnitTests.Media.TextFormatting { public class LineBreakerTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/TestDataGenerator.cs b/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/TestDataGenerator.cs new file mode 100644 index 0000000000..058de909df --- /dev/null +++ b/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/TestDataGenerator.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; + +namespace Avalonia.Visuals.UnitTests.Media.TextFormatting +{ + public abstract class TestDataGenerator : IEnumerable + { + private readonly string _fileName; + private readonly List _testData; + + protected TestDataGenerator(string fileName) + { + _fileName = fileName; + _testData = ReadTestData(); + } + + public IEnumerator GetEnumerator() + { + return _testData.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + private List ReadTestData() + { + var testData = new List(); + + using (var client = new HttpClient()) + { + var url = Path.Combine(UnicodeDataGenerator.Ucd, _fileName); + + using (var result = client.GetAsync(url).GetAwaiter().GetResult()) + { + if (!result.IsSuccessStatusCode) + return testData; + + using (var stream = result.Content.ReadAsStreamAsync().GetAwaiter().GetResult()) + using (var reader = new StreamReader(stream)) + { + while (!reader.EndOfStream) + { + var line = reader.ReadLine(); + + if (line == null) + { + break; + } + + if (line.StartsWith("#") || string.IsNullOrEmpty(line)) + { + continue; + } + + var elements = line.Split('#'); + + elements = elements[0].Replace("÷\t", "÷").Trim('÷').Split('÷'); + + var chars = elements[0].Replace(" × ", " ").Split(' '); + + var codepoints = chars.Where(x => x != "" && x != "×") + .Select(x => Convert.ToInt32(x, 16)).ToArray(); + + var text = string.Join(null, codepoints.Select(char.ConvertFromUtf32)); + + var length = codepoints.Select(x => x > ushort.MaxValue ? 2 : 1).Sum(); + + var data = new object[] { text, length }; + + testData.Add(data); + } + } + } + } + + return testData; + } + } +} diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeDataGenerator.cs b/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeDataGenerator.cs index f7b8b68ab3..cbe8edefb6 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeDataGenerator.cs +++ b/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeDataGenerator.cs @@ -9,13 +9,16 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting { internal static class UnicodeDataGenerator { + public const string Ucd = "https://www.unicode.org/Public/13.0.0/ucd/"; + public static void Execute() { var codepoints = new Dictionary(); - var generalCategoryValues = UnicodeEnumsGenerator.CreateGeneralCategoryEnum(); + var generalCategoryEntries = + UnicodeEnumsGenerator.CreateGeneralCategoryEnum(); - var generalCategoryMappings = CreateTagToIndexMappings(generalCategoryValues); + var generalCategoryMappings = CreateTagToIndexMappings(generalCategoryEntries); var generalCategoryData = ReadGeneralCategoryData(); @@ -26,23 +29,23 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting AddGeneralCategoryRange(codepoints, range, generalCategory); } - var scriptValues = UnicodeEnumsGenerator.CreateScriptEnum(); + var scriptEntries = UnicodeEnumsGenerator.CreateScriptEnum(); - var scriptMappings = CreateNameToIndexMappings(scriptValues); + var scriptMappings = CreateNameToIndexMappings(scriptEntries); var scriptData = ReadScriptData(); foreach (var (range, name) in scriptData) { - var script = scriptMappings[name.Replace("_", "")]; + var script = scriptMappings[name]; AddScriptRange(codepoints, range, script); - } - var biDiClassValues = UnicodeEnumsGenerator.CreateBiDiClassEnum(); + var biDiClassEntries = + UnicodeEnumsGenerator.CreateBiDiClassEnum(); - var biDiClassMappings = CreateTagToIndexMappings(biDiClassValues); + var biDiClassMappings = CreateTagToIndexMappings(biDiClassEntries); var biDiData = ReadBiDiData(); @@ -53,9 +56,10 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting AddBiDiClassRange(codepoints, range, biDiClass); } - var lineBreakClassValues = UnicodeEnumsGenerator.CreateLineBreakClassEnum(); + var lineBreakClassEntries = + UnicodeEnumsGenerator.CreateLineBreakClassEnum(); - var lineBreakClassMappings = CreateTagToIndexMappings(lineBreakClassValues); + var lineBreakClassMappings = CreateTagToIndexMappings(lineBreakClassEntries); var lineBreakClassData = ReadLineBreakClassData(); @@ -66,11 +70,11 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting AddLineBreakClassRange(codepoints, range, lineBreakClass); } - const int initialValue = ((int)LineBreakClass.Unknown << UnicodeData.LINEBREAK_SHIFT) | - ((int)BiDiClass.LeftToRight << UnicodeData.BIDI_SHIFT) | - ((int)Script.Unknown << UnicodeData.SCRIPT_SHIFT) | (int)GeneralCategory.Other; + //const int initialValue = (0 << UnicodeData.LINEBREAK_SHIFT) | + // (0 << UnicodeData.BIDI_SHIFT) | + // (0 << UnicodeData.SCRIPT_SHIFT) | (int)GeneralCategory.Other; - var builder = new UnicodeTrieBuilder(initialValue); + var builder = new UnicodeTrieBuilder(/*initialValue*/); foreach (var properties in codepoints.Values) { @@ -88,27 +92,30 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting trie.Save(stream); } + + UnicodeEnumsGenerator.CreatePropertyValueAliasHelper(scriptEntries, generalCategoryEntries, + biDiClassEntries, lineBreakClassEntries); } - private static Dictionary CreateTagToIndexMappings(List<(string name, string tag, string comment)> values) + private static Dictionary CreateTagToIndexMappings(List entries) { var mappings = new Dictionary(); - for (var i = 0; i < values.Count; i++) + for (var i = 0; i < entries.Count; i++) { - mappings.Add(values[i].tag, i); + mappings.Add(entries[i].Tag, i); } return mappings; } - private static Dictionary CreateNameToIndexMappings(List<(string name, string tag, string comment)> values) + private static Dictionary CreateNameToIndexMappings(List entries) { var mappings = new Dictionary(); - for (var i = 0; i < values.Count; i++) + for (var i = 0; i < entries.Count; i++) { - mappings.Add(values[i].name, i); + mappings.Add(entries[i].Name, i); } return mappings; @@ -180,24 +187,22 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting public static List<(CodepointRange, string)> ReadGeneralCategoryData() { - return ReadUnicodeData( - "https://www.unicode.org/Public/UCD/latest/ucd/extracted/DerivedGeneralCategory.txt"); + return ReadUnicodeData("extracted/DerivedGeneralCategory.txt"); } public static List<(CodepointRange, string)> ReadScriptData() { - return ReadUnicodeData("https://www.unicode.org/Public/UCD/latest/ucd/Scripts.txt"); + return ReadUnicodeData("Scripts.txt"); } public static List<(CodepointRange, string)> ReadBiDiData() { - return ReadUnicodeData("https://www.unicode.org/Public/UCD/latest/ucd/extracted/DerivedBidiClass.txt"); + return ReadUnicodeData("extracted/DerivedBidiClass.txt"); } public static List<(CodepointRange, string)> ReadLineBreakClassData() { - return ReadUnicodeData( - "https://www.unicode.org/Public/UCD/latest/ucd/extracted/DerivedLineBreak.txt"); + return ReadUnicodeData("extracted/DerivedLineBreak.txt"); } private static List<(CodepointRange, string)> ReadUnicodeData(string file) @@ -208,7 +213,9 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting using (var client = new HttpClient()) { - using (var result = client.GetAsync(file).GetAwaiter().GetResult()) + var url = Path.Combine(Ucd, file); + + using (var result = client.GetAsync(url).GetAwaiter().GetResult()) { if (!result.IsSuccessStatusCode) { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeDataGeneratorTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeDataGeneratorTests.cs index 47aef84533..5c705ba0c7 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeDataGeneratorTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeDataGeneratorTests.cs @@ -1,4 +1,6 @@ -using Xunit; +using System; +using Avalonia.Media.TextFormatting.Unicode; +using Xunit; namespace Avalonia.Visuals.UnitTests.Media.TextFormatting { @@ -13,5 +15,26 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting { UnicodeDataGenerator.Execute(); } + [Theory(Skip = "Only run when we update the trie.")] + [ClassData(typeof(LineBreakTestDataGenerator))] + + public void Should_Enumerate_LineBreaks(string text, int expectedLength) + { + var textMemory = text.AsMemory(); + + var enumerator = new LineBreakEnumerator(textMemory); + + Assert.True(enumerator.MoveNext()); + + Assert.Equal(expectedLength, enumerator.Current.PositionWrap); + } + + private class LineBreakTestDataGenerator : TestDataGenerator + { + public LineBreakTestDataGenerator() + : base("auxiliary/LineBreakTest.txt") + { + } + } } } diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeEnumsGenerator.cs b/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeEnumsGenerator.cs index e141204d4c..3a936ff3b0 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeEnumsGenerator.cs +++ b/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeEnumsGenerator.cs @@ -8,9 +8,16 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting { internal static class UnicodeEnumsGenerator { - public static List<(string name, string tag, string comment)> CreateScriptEnum() + public static List CreateScriptEnum() { - var scriptValues = GetPropertyValueAliases("# Script (sc)"); + var entries = new List + { + new DataEntry("Unknown", "Zzzz", string.Empty), + new DataEntry("Common", "Zyyy", string.Empty), + new DataEntry("Inherited", "Zinh", string.Empty) + }; + + ParseDataEntries("# Script (sc)", entries); using (var stream = File.Create("Generated\\Script.cs")) using (var writer = new StreamWriter(stream)) @@ -20,22 +27,24 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting writer.WriteLine(" public enum Script"); writer.WriteLine(" {"); - foreach (var (name, tag, comment) in scriptValues) + foreach (var entry in entries) { - writer.WriteLine(" " + name + ", //" + tag + - (string.IsNullOrEmpty(comment) ? string.Empty : "#" + comment)); + writer.WriteLine(" " + entry.Name.Replace("_", "") + ", //" + entry.Tag + + (string.IsNullOrEmpty(entry.Comment) ? string.Empty : "#" + entry.Comment)); } writer.WriteLine(" }"); writer.WriteLine("}"); } - return scriptValues; + return entries; } - public static List<(string name, string tag, string comment)> CreateGeneralCategoryEnum() + public static List CreateGeneralCategoryEnum() { - var generalCategoryValues = GetPropertyValueAliases("# General_Category (gc)"); + var entries = new List { new DataEntry("Other", "C", " Cc | Cf | Cn | Co | Cs") }; + + ParseDataEntries("# General_Category (gc)", entries); using (var stream = File.Create("Generated\\GeneralCategory.cs")) using (var writer = new StreamWriter(stream)) @@ -45,22 +54,24 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting writer.WriteLine(" public enum GeneralCategory"); writer.WriteLine(" {"); - foreach (var (name, tag, comment) in generalCategoryValues) + foreach (var entry in entries) { - writer.WriteLine(" " + name + ", //" + tag + - (string.IsNullOrEmpty(comment) ? string.Empty : "#" + comment)); + writer.WriteLine(" " + entry.Name.Replace("_", "") + ", //" + entry.Tag + + (string.IsNullOrEmpty(entry.Comment) ? string.Empty : "#" + entry.Comment)); } writer.WriteLine(" }"); writer.WriteLine("}"); } - return generalCategoryValues; + return entries; } - public static List<(string name, string tag, string comment)> CreateGraphemeBreakTypeEnum() + public static List CreateGraphemeBreakTypeEnum() { - var graphemeClusterBreakValues = GetPropertyValueAliases("# Grapheme_Cluster_Break (GCB)"); + var entries = new List { new DataEntry("Other", "XX", string.Empty) }; + + ParseDataEntries("# Grapheme_Cluster_Break (GCB)", entries); using (var stream = File.Create("Generated\\GraphemeBreakClass.cs")) using (var writer = new StreamWriter(stream)) @@ -70,10 +81,10 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting writer.WriteLine(" public enum GraphemeBreakClass"); writer.WriteLine(" {"); - foreach (var (name, tag, comment) in graphemeClusterBreakValues) + foreach (var entry in entries) { - writer.WriteLine(" " + name + ", //" + tag + - (string.IsNullOrEmpty(comment) ? string.Empty : "#" + comment)); + writer.WriteLine(" " + entry.Name.Replace("_", "") + ", //" + entry.Tag + + (string.IsNullOrEmpty(entry.Comment) ? string.Empty : "#" + entry.Comment)); } writer.WriteLine(" ExtendedPictographic"); @@ -82,7 +93,7 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting writer.WriteLine("}"); } - return graphemeClusterBreakValues; + return entries; } private static List GenerateBreakPairTable() @@ -185,20 +196,32 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting } } - public static List<(string name, string tag, string comment)> CreateLineBreakClassEnum() + public static List CreateLineBreakClassEnum() { var usedLineBreakClasses = GenerateBreakPairTable(); - var lineBreakValues = GetPropertyValueAliases("# Line_Break (lb)"); + var entries = new List { new DataEntry("Unknown", "XX", string.Empty) }; + + ParseDataEntries("# Line_Break (lb)", entries); - var lineBreakClassMappings = lineBreakValues.ToDictionary(x => x.tag, x => (x.name, x.tag, x.comment)); + var orderedLineBreakEntries = new Dictionary(); - var orderedLineBreakValues = usedLineBreakClasses.Select(x => + foreach (var tag in usedLineBreakClasses) { - var value = lineBreakClassMappings[x]; - lineBreakClassMappings.Remove(x); - return value; - }).ToList(); + var entry = entries.Single(x => x.Tag == tag); + + orderedLineBreakEntries.Add(tag, entry); + } + + foreach (var entry in entries) + { + if (orderedLineBreakEntries.ContainsKey(entry.Tag)) + { + continue; + } + + orderedLineBreakEntries.Add(entry.Tag, entry); + } using (var stream = File.Create("Generated\\LineBreakClass.cs")) using (var writer = new StreamWriter(stream)) @@ -208,32 +231,24 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting writer.WriteLine(" public enum LineBreakClass"); writer.WriteLine(" {"); - foreach (var (name, tag, comment) in orderedLineBreakValues) - { - writer.WriteLine(" " + name + ", //" + tag + - (string.IsNullOrEmpty(comment) ? string.Empty : "#" + comment)); - } - - writer.WriteLine(); - - foreach (var (name, tag, comment) in lineBreakClassMappings.Values) + foreach (var entry in orderedLineBreakEntries.Values) { - writer.WriteLine(" " + name + ", //" + tag + - (string.IsNullOrEmpty(comment) ? string.Empty : "#" + comment)); + writer.WriteLine(" " + entry.Name.Replace("_", "") + ", //" + entry.Tag + + (string.IsNullOrEmpty(entry.Comment) ? string.Empty : "#" + entry.Comment)); } writer.WriteLine(" }"); writer.WriteLine("}"); } - orderedLineBreakValues.AddRange(lineBreakClassMappings.Values); - - return orderedLineBreakValues; + return orderedLineBreakEntries.Values.ToList(); } - public static List<(string name, string tag, string comment)> CreateBiDiClassEnum() + public static List CreateBiDiClassEnum() { - var biDiClassValues = GetPropertyValueAliases("# Bidi_Class (bc)"); + var entries = new List { new DataEntry("Left_To_Right", "L", string.Empty) }; + + ParseDataEntries("# Bidi_Class (bc)", entries); using (var stream = File.Create("Generated\\BiDiClass.cs")) using (var writer = new StreamWriter(stream)) @@ -243,23 +258,21 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting writer.WriteLine(" public enum BiDiClass"); writer.WriteLine(" {"); - foreach (var (name, tag, comment) in biDiClassValues) + foreach (var entry in entries) { - writer.WriteLine(" " + name + ", //" + tag + - (string.IsNullOrEmpty(comment) ? string.Empty : "#" + comment)); + writer.WriteLine(" " + entry.Name.Replace("_", "") + ", //" + entry.Tag + + (string.IsNullOrEmpty(entry.Comment) ? string.Empty : "#" + entry.Comment)); } writer.WriteLine(" }"); writer.WriteLine("}"); } - return biDiClassValues; + return entries; } - public static void CreatePropertyValueAliasHelper(List<(string name, string tag, string comment)> scriptValues, - List<(string name, string tag, string comment)> generalCategoryValues, - List<(string name, string tag, string comment)> biDiClassValues, - List<(string name, string tag, string comment)> lineBreakValues) + public static void CreatePropertyValueAliasHelper(List scriptEntries, IEnumerable generalCategoryEntries, + IEnumerable biDiClassEntries, IEnumerable lineBreakClassEntries) { using (var stream = File.Create("Generated\\PropertyValueAliasHelper.cs")) using (var writer = new StreamWriter(stream)) @@ -269,35 +282,35 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting writer.WriteLine("namespace Avalonia.Media.TextFormatting.Unicode"); writer.WriteLine("{"); - writer.WriteLine(" public static class PropertyValueAliasHelper"); + writer.WriteLine(" internal static class PropertyValueAliasHelper"); writer.WriteLine(" {"); - WritePropertyValueAliasGetTag(writer, scriptValues, "Script", "Zzzz"); + WritePropertyValueAliasGetTag(writer, scriptEntries, "Script", "Zzzz"); - WritePropertyValueAlias(writer, scriptValues, "Script", "Unknown"); + WritePropertyValueAlias(writer, scriptEntries, "Script", "Unknown"); - WritePropertyValueAlias(writer, generalCategoryValues, "GeneralCategory", "Other"); + WritePropertyValueAlias(writer, generalCategoryEntries, "GeneralCategory", "Other"); - WritePropertyValueAlias(writer, biDiClassValues, "BiDiClass", "LeftToRight"); + WritePropertyValueAlias(writer, biDiClassEntries, "BiDiClass", "LeftToRight"); - WritePropertyValueAlias(writer, lineBreakValues, "LineBreakClass", "Unknown"); + WritePropertyValueAlias(writer, lineBreakClassEntries, "LineBreakClass", "Unknown"); writer.WriteLine(" }"); writer.WriteLine("}"); } } - public static List<(string name, string tag, string comment)> GetPropertyValueAliases(string property) + public static void ParseDataEntries(string property, List entries) { - var data = new List<(string name, string tag, string comment)>(); - using (var client = new HttpClient()) { - using (var result = client.GetAsync("https://www.unicode.org/Public/UCD/latest/ucd/PropertyValueAliases.txt").GetAwaiter().GetResult()) + var url = Path.Combine(UnicodeDataGenerator.Ucd, "PropertyValueAliases.txt"); + + using (var result = client.GetAsync(url).GetAwaiter().GetResult()) { if (!result.IsSuccessStatusCode) { - return data; + return; } using (var stream = result.Content.ReadAsStreamAsync().GetAwaiter().GetResult()) @@ -337,7 +350,12 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting elements = elements[2].Split('#'); - var name = elements[0].Trim().Replace("_", string.Empty); + var name = elements[0].Trim(); + + if (entries.Any(x => x.Name == name)) + { + continue; + } var comment = string.Empty; @@ -346,24 +364,25 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting comment = elements[1]; } - data.Add((name, tag, comment)); + var entry = new DataEntry(name, tag, comment); + + entries.Add(entry); } } } } - - return data; } - private static void WritePropertyValueAliasGetTag(TextWriter writer, - IEnumerable<(string name, string tag, string comment)> values, string typeName, string defaultValue) + private static void WritePropertyValueAliasGetTag(TextWriter writer, IEnumerable entries, + string typeName, string defaultValue) { - writer.WriteLine($" private static readonly Dictionary<{typeName}, string> s_{typeName.ToLower()}ToTag = "); + writer.WriteLine( + $" private static readonly Dictionary<{typeName}, string> s_{typeName.ToLower()}ToTag = "); writer.WriteLine($" new Dictionary<{typeName}, string>{{"); - foreach (var (name, tag, comment) in values) + foreach (var entry in entries) { - writer.WriteLine($" {{ {typeName}.{name}, \"{tag}\"}},"); + writer.WriteLine($" {{ {typeName}.{entry.Name.Replace("_", "")}, \"{entry.Tag}\"}},"); } writer.WriteLine(" };"); @@ -382,15 +401,15 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting writer.WriteLine(); } - private static void WritePropertyValueAlias(TextWriter writer, - IEnumerable<(string name, string tag, string comment)> values, string typeName, string defaultValue) + private static void WritePropertyValueAlias(TextWriter writer, IEnumerable entries, string typeName, + string defaultValue) { writer.WriteLine($" private static readonly Dictionary s_tagTo{typeName} = "); writer.WriteLine($" new Dictionary{{"); - foreach (var (name, tag, comment) in values) + foreach (var entry in entries) { - writer.WriteLine($" {{ \"{tag}\", {typeName}.{name}}},"); + writer.WriteLine($" {{ \"{entry.Tag}\", {typeName}.{entry.Name.Replace("_", "")}}},"); } writer.WriteLine(" };"); @@ -409,4 +428,18 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting writer.WriteLine(); } } + + public readonly struct DataEntry + { + public DataEntry(string name, string tag, string comment) + { + Name = name; + Tag = tag; + Comment = comment; + } + + public string Name { get; } + public string Tag { get; } + public string Comment { get; } + } }