Browse Source

Merge branch 'master' into foreign-embed-createahead

pull/4193/head
danwalmsley 6 years ago
committed by GitHub
parent
commit
063afe4a23
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      Avalonia.sln
  2. 40
      src/Avalonia.Layout/LayoutManager.cs
  3. 38
      src/Avalonia.Themes.Fluent/Accents/FluentBaseDark.xaml
  4. 39
      src/Avalonia.Themes.Fluent/Accents/FluentBaseLight.xaml
  5. BIN
      src/Avalonia.Visuals/Assets/GraphemeBreak.trie
  6. BIN
      src/Avalonia.Visuals/Assets/UnicodeData.trie
  7. 2
      src/Avalonia.Visuals/Media/TextFormatting/Unicode/BiDiClass.cs
  8. 65
      src/Avalonia.Visuals/Media/TextFormatting/Unicode/BreakPairTable.cs
  9. 34
      src/Avalonia.Visuals/Media/TextFormatting/Unicode/GraphemeBreakClass.cs
  10. 4
      src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreakClass.cs
  11. 4
      src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreakEnumerator.cs
  12. 178
      src/Avalonia.Visuals/Media/TextFormatting/Unicode/PropertyValueAliasHelper.cs
  13. 10
      src/Avalonia.Visuals/Media/TextFormatting/Unicode/Script.cs
  14. 67
      tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/BreakPairTable.txt
  15. 61
      tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGenerator.cs
  16. 83
      tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGeneratorTests.cs
  17. 2
      tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/LineBreakerTests.cs
  18. 85
      tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/TestDataGenerator.cs
  19. 61
      tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeDataGenerator.cs
  20. 25
      tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeDataGeneratorTests.cs
  21. 181
      tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeEnumsGenerator.cs

8
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

40
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)

38
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">
<Style.Resources>
<Color x:Key="SystemAccentColor">#FF0078D7</Color>
<Color x:Key="SystemAltHighColor">#FF000000</Color>
<Color x:Key="SystemAltLowColor">#FF000000</Color>
<Color x:Key="SystemAltMediumColor">#FF000000</Color>
<Color x:Key="SystemAltMediumHighColor">#FF000000</Color>
<Color x:Key="SystemAltMediumLowColor">#FF000000</Color>
<Color x:Key="SystemBaseHighColor">#FFFFFFFF</Color>
<Color x:Key="SystemBaseLowColor">#FF333333</Color>
<Color x:Key="SystemBaseMediumColor">#FF9A9A9A</Color>
<Color x:Key="SystemBaseMediumHighColor">#FFB4B4B4</Color>
<Color x:Key="SystemBaseMediumLowColor">#FF676767</Color>
<Color x:Key="SystemChromeAltLowColor">#FFB4B4B4</Color>
<Color x:Key="SystemChromeBlackHighColor">#FF000000</Color>
<Color x:Key="SystemChromeBlackLowColor">#FFB4B4B4</Color>
<Color x:Key="SystemChromeBlackMediumColor">#FF000000</Color>
<Color x:Key="SystemChromeBlackMediumLowColor">#FF000000</Color>
<Color x:Key="SystemChromeDisabledHighColor">#FF333333</Color>
<Color x:Key="SystemChromeGrayColor">#FF808080</Color>
<Color x:Key="SystemChromeHighColor">#FF808080</Color>
<Color x:Key="SystemChromeLowColor">#FF151515</Color>
<Color x:Key="SystemChromeMediumColor">#FF1D1D1D</Color>
<Color x:Key="SystemChromeMediumLowColor">#FF2C2C2C</Color>
<Color x:Key="SystemChromeWhiteColor">#FFFFFFFF</Color>
<Color x:Key="SystemListLowColor">#FF1D1D1D</Color>
<Color x:Key="SystemListMediumColor">#FF333333</Color>
<Color x:Key="SystemChromeAltMediumHighColor">#CC000000</Color>
<Color x:Key="SystemChromeAltHighColor">#FF333333</Color>
<Color x:Key="SystemRevealListLowColor">#FF1D1D1D</Color>
<Color x:Key="SystemRevealListMediumColor">#FF333333</Color>
<Color x:Key="SystemRevealListLowColor">#18FFFFFF</Color>
<Color x:Key="SystemRevealListMediumColor">#30FFFFFF</Color>
<!--<AcrylicBrush x:Key="SystemControlAcrylicWindowBrush" BackgroundSource="HostBackdrop" TintColor="{ThemeResource SystemChromeAltHighColor}" TintOpacity="0.8" FallbackColor="{ThemeResource SystemChromeMediumColor}" />-->
<!--<RevealBackgroundBrush x:Key="SystemControlTransparentRevealBackgroundBrush" TargetTheme="Dark" Color="Transparent" FallbackColor="Transparent" />-->
<SolidColorBrush x:Key="SystemControlTransparentRevealBackgroundBrush" Color="Transparent" />
@ -67,13 +40,6 @@
<Thickness x:Key="ComboBoxItemRevealBorderThemeThickness">1,1,1,1</Thickness>
<x:Double x:Key="PersonPictureEllipseBadgeStrokeThickness">1</x:Double>
<!-- Override system generated accent colors -->
<Color x:Key="SystemAccentColorDark1">#FF005A9E</Color>
<Color x:Key="SystemAccentColorDark2">#FF004275</Color>
<Color x:Key="SystemAccentColorDark3">#FF002642</Color>
<Color x:Key="SystemAccentColorLight1">#FF429CE3</Color>
<Color x:Key="SystemAccentColorLight2">#FF76B9ED</Color>
<Color x:Key="SystemAccentColorLight3">#FFA6D8FF</Color>
<Color x:Key="RegionColor">#FF000000</Color>
<SolidColorBrush x:Key="RegionBrush" Color="{StaticResource RegionColor}" />

39
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">
<Style.Resources>
<Color x:Key="SystemAccentColor">#FF0078D7</Color>
<Color x:Key="SystemAltHighColor">#FFFFFFFF</Color>
<Color x:Key="SystemAltLowColor">#FFFFFFFF</Color>
<Color x:Key="SystemAltMediumColor">#FFFFFFFF</Color>
<Color x:Key="SystemAltMediumHighColor">#FFFFFFFF</Color>
<Color x:Key="SystemAltMediumLowColor">#FFFFFFFF</Color>
<Color x:Key="SystemBaseHighColor">#FF000000</Color>
<Color x:Key="SystemBaseLowColor">#FFCCCCCC</Color>
<Color x:Key="SystemBaseMediumColor">#FF898989</Color>
<Color x:Key="SystemBaseMediumHighColor">#FF5D5D5D</Color>
<Color x:Key="SystemBaseMediumLowColor">#FF737373</Color>
<Color x:Key="SystemChromeAltLowColor">#FF5D5D5D</Color>
<Color x:Key="SystemChromeBlackHighColor">#FF000000</Color>
<Color x:Key="SystemChromeBlackLowColor">#FFCCCCCC</Color>
<Color x:Key="SystemChromeBlackMediumColor">#FF5D5D5D</Color>
<Color x:Key="SystemChromeBlackMediumLowColor">#FF898989</Color>
<Color x:Key="SystemChromeDisabledHighColor">#FFCCCCCC</Color>
<Color x:Key="SystemChromeDisabledLowColor">#FF898989</Color>
<Color x:Key="SystemChromeGrayColor">#FF737373</Color>
<Color x:Key="SystemChromeHighColor">#FFCCCCCC</Color>
<Color x:Key="SystemChromeLowColor">#FFECECEC</Color>
<Color x:Key="SystemChromeMediumColor">#FFE6E6E6</Color>
<Color x:Key="SystemChromeMediumLowColor">#FFECECEC</Color>
<Color x:Key="SystemChromeWhiteColor">#FFFFFFFF</Color>
<Color x:Key="SystemListLowColor">#FFE6E6E6</Color>
<Color x:Key="SystemListMediumColor">#FFCCCCCC</Color>
<Color x:Key="SystemChromeAltMediumHighColor">#CCFFFFFF</Color>
<Color x:Key="SystemChromeAltHighColor">#FFCCCCCC</Color>
<Color x:Key="SystemRevealListLowColor">#FFE6E6E6</Color>
<Color x:Key="SystemRevealListMediumColor">#FFCCCCCC</Color>
<Color x:Key="SystemRevealListLowColor">#17000000</Color>
<Color x:Key="SystemRevealListMediumColor">#2E000000</Color>
<!--<AcrylicBrush x:Key="SystemControlAcrylicWindowBrush" BackgroundSource="HostBackdrop" TintColor="{ThemeResource SystemChromeAltHighColor}" TintOpacity="0.8" FallbackColor="{ThemeResource SystemChromeMediumColor}" />-->
<!--<RevealBackgroundBrush x:Key="SystemControlTransparentRevealBackgroundBrush" TargetTheme="Dark" Color="Transparent" FallbackColor="Transparent" />-->
<SolidColorBrush x:Key="SystemControlTransparentRevealBackgroundBrush" Color="Transparent" />
@ -68,13 +40,6 @@
<Thickness x:Key="ComboBoxItemRevealBorderThemeThickness">1,1,1,1</Thickness>
<x:Double x:Key="PersonPictureEllipseBadgeStrokeThickness">1</x:Double>
<!-- Override system generated accent colors -->
<Color x:Key="SystemAccentColorDark1">#FF005A9E</Color>
<Color x:Key="SystemAccentColorDark2">#FF004275</Color>
<Color x:Key="SystemAccentColorDark3">#FF002642</Color>
<Color x:Key="SystemAccentColorLight1">#FF429CE3</Color>
<Color x:Key="SystemAccentColorLight2">#FF76B9ED</Color>
<Color x:Key="SystemAccentColorLight3">#FFA6D8FF</Color>
<!--<RevealBackgroundBrush x:Key="SystemControlHighlightListLowRevealBackgroundBrush" TargetTheme="Light" Color="{ThemeResource SystemRevealListMediumColor}" FallbackColor="{ StaticResource SystemListMediumColor}" />-->
<Color x:Key="RegionColor">#FFFFFFFF</Color>
<SolidColorBrush x:Key="RegionBrush" Color="{StaticResource RegionColor}" />

BIN
src/Avalonia.Visuals/Assets/GraphemeBreak.trie

Binary file not shown.

BIN
src/Avalonia.Visuals/Assets/UnicodeData.trie

Binary file not shown.

2
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

65
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)

34
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,
}
}

4
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
}
}

4
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

178
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<Script, string> s_scriptToTag =
new Dictionary<Script, string>{
{ 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];
}
}
}

10
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
}
}

67
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 _ ^ ^ % % _ ^ ^ ^ _ _ _ _ _ _ _ _ _ _ _ ^ # ^ _ _ _ _ _ _ _ _ % _

61
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<GraphemeBreakClass>(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));
}
}
}

83
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<object[]>
private class GraphemeBreakTestDataGenerator : TestDataGenerator
{
private readonly List<object[]> _testData;
public GraphemeEnumeratorTestDataGenerator()
{
_testData = ReadTestData();
}
public IEnumerator<object[]> GetEnumerator()
{
return _testData.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private static List<object[]> ReadTestData()
public GraphemeBreakTestDataGenerator()
: base("auxiliary/GraphemeBreakTest.txt")
{
var testData = new List<object[]>();
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;
}
}
}

2
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
{

85
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<object[]>
{
private readonly string _fileName;
private readonly List<object[]> _testData;
protected TestDataGenerator(string fileName)
{
_fileName = fileName;
_testData = ReadTestData();
}
public IEnumerator<object[]> GetEnumerator()
{
return _testData.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private List<object[]> ReadTestData()
{
var testData = new List<object[]>();
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;
}
}
}

61
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<int, UnicodeDataItem>();
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<string, int> CreateTagToIndexMappings(List<(string name, string tag, string comment)> values)
private static Dictionary<string, int> CreateTagToIndexMappings(List<DataEntry> entries)
{
var mappings = new Dictionary<string, int>();
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<string, int> CreateNameToIndexMappings(List<(string name, string tag, string comment)> values)
private static Dictionary<string, int> CreateNameToIndexMappings(List<DataEntry> entries)
{
var mappings = new Dictionary<string, int>();
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)
{

25
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")
{
}
}
}
}

181
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<DataEntry> CreateScriptEnum()
{
var scriptValues = GetPropertyValueAliases("# Script (sc)");
var entries = new List<DataEntry>
{
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<DataEntry> CreateGeneralCategoryEnum()
{
var generalCategoryValues = GetPropertyValueAliases("# General_Category (gc)");
var entries = new List<DataEntry> { 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<DataEntry> CreateGraphemeBreakTypeEnum()
{
var graphemeClusterBreakValues = GetPropertyValueAliases("# Grapheme_Cluster_Break (GCB)");
var entries = new List<DataEntry> { 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<string> GenerateBreakPairTable()
@ -185,20 +196,32 @@ namespace Avalonia.Visuals.UnitTests.Media.TextFormatting
}
}
public static List<(string name, string tag, string comment)> CreateLineBreakClassEnum()
public static List<DataEntry> CreateLineBreakClassEnum()
{
var usedLineBreakClasses = GenerateBreakPairTable();
var lineBreakValues = GetPropertyValueAliases("# Line_Break (lb)");
var entries = new List<DataEntry> { 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<string, DataEntry>();
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<DataEntry> CreateBiDiClassEnum()
{
var biDiClassValues = GetPropertyValueAliases("# Bidi_Class (bc)");
var entries = new List<DataEntry> { 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<DataEntry> scriptEntries, IEnumerable<DataEntry> generalCategoryEntries,
IEnumerable<DataEntry> biDiClassEntries, IEnumerable<DataEntry> 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<DataEntry> 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<DataEntry> 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<DataEntry> entries, string typeName,
string defaultValue)
{
writer.WriteLine($" private static readonly Dictionary<string, {typeName}> s_tagTo{typeName} = ");
writer.WriteLine($" new Dictionary<string,{typeName}>{{");
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; }
}
}

Loading…
Cancel
Save