Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Svg.Skia.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
<Project Path="tests/ShimSkiaSharp.UnitTests/ShimSkiaSharp.UnitTests.csproj" />
<Project Path="tests/Svg.Skia.Benchmarks/Svg.Skia.Benchmarks.csproj" />
<Project Path="tests/Svg.Controls.Avalonia.UnitTests/Svg.Controls.Avalonia.UnitTests.csproj" />
<Project Path="tests/Svg.Controls.Skia.Avalonia.InvariantGlobalization.UnitTests/Svg.Controls.Skia.Avalonia.InvariantGlobalization.UnitTests.csproj" />
<Project Path="tests/Svg.Controls.Skia.Avalonia.UnitTests/Svg.Controls.Skia.Avalonia.UnitTests.csproj" />
<Project Path="tests/Svg.Controls.Skia.Uno.UnitTests/Svg.Controls.Skia.Uno.UnitTests.csproj" />
<Project Path="tests/Svg.Editor.Skia.Avalonia.UnitTests/Svg.Editor.Skia.Avalonia.UnitTests.csproj" />
Expand Down
29 changes: 26 additions & 3 deletions src/Svg.Model/Services/SvgService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ namespace Svg.Model.Services;

public static class SvgService
{
public static CultureInfo? s_systemLanguageOverride = CultureInfo.GetCultureInfo("en-US");
private const string DefaultSystemLanguageTag = "en-US";

public static CultureInfo? s_systemLanguageOverride = TryGetCultureInfo(DefaultSystemLanguageTag);

private static readonly char[] s_spaceTab = { ' ', '\t' };

Expand Down Expand Up @@ -431,8 +433,7 @@ internal static bool HasSystemLanguage(this SvgElement svgElement)
return false;
}

var systemLanguage = s_systemLanguageOverride ?? CultureInfo.InstalledUICulture;
var systemLanguageTag = GetSystemLanguageTag(systemLanguage);
var systemLanguageTag = GetCurrentSystemLanguageTag();
if (string.IsNullOrWhiteSpace(systemLanguageTag))
{
return false;
Expand All @@ -449,6 +450,28 @@ internal static bool HasSystemLanguage(this SvgElement svgElement)
return false;
}

private static CultureInfo? TryGetCultureInfo(string name)
{
try
{
return CultureInfo.GetCultureInfo(name);
}
catch (CultureNotFoundException)
{
return null;
}
}

private static string? GetCurrentSystemLanguageTag()
{
if (s_systemLanguageOverride is { } systemLanguageOverride)
{
return GetSystemLanguageTag(systemLanguageOverride);
}

return GetSystemLanguageTag(CultureInfo.InstalledUICulture) ?? DefaultSystemLanguageTag;
}

internal static bool PassesConditionalProcessing(this SvgElement svgElement, DrawAttributes ignoreAttributes)
{
var hasRequiredFeatures = ignoreAttributes.Has(DrawAttributes.RequiredFeatures) || svgElement.HasRequiredFeatures();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<OutputType>Exe</OutputType>
<IsPackable>False</IsPackable>
<IsTestProject>true</IsTestProject>
<Nullable>enable</Nullable>
<RootNamespace>Avalonia.Svg.Skia.InvariantGlobalization.UnitTests</RootNamespace>
<InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>

<Import Project="..\..\build\ReferenceAssemblies.props" />
<Import Project="..\..\build\XUnit.v3.props" />
<Import Project="..\..\build\SkiaSharp.props" />
<Import Project="..\..\build\SkiaSharp.Native.props" />
<Import Project="..\..\build\SkiaSharp.Avalonia.props" />
<Import Project="..\..\build\Avalonia.props" />
<Import Project="..\..\build\Avalonia.Skia.props" />

<ItemGroup>
<ProjectReference Include="..\..\src\Svg.Controls.Skia.Avalonia\Svg.Controls.Skia.Avalonia.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
using System.Globalization;
using System.IO;
using Avalonia.Svg.Skia;
using SkiaSharp;
using Xunit;

namespace Avalonia.Svg.Skia.InvariantGlobalization.UnitTests;

public class SvgSourceInvariantGlobalizationTests
{
private const string SampleSvg = """
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10">
<rect x="0" y="0" width="10" height="10" fill="red" />
</svg>
""";

[Fact]
public void LoadFromSvg_CreatesPicture_WhenGlobalizationInvariant()
{
AssertInvariantGlobalization();

using var source = SvgSource.LoadFromSvg(SampleSvg);

Assert.NotNull(source.Svg);
Assert.NotNull(source.Picture);
}

[Fact]
public void Load_FilePathCreatesPicture_WhenGlobalizationInvariant()
{
AssertInvariantGlobalization();

var path = Path.Combine(Path.GetTempPath(), $"{Path.GetRandomFileName()}.svg");
File.WriteAllText(path, SampleSvg);

try
{
using var source = SvgSource.Load(path);

Assert.NotNull(source.Svg);
Assert.NotNull(source.Picture);
}
finally
{
File.Delete(path);
}
}

[Fact]
public void LoadFromSvg_UsesDeterministicDefaultSystemLanguage_WhenGlobalizationInvariant()
{
AssertInvariantGlobalization();

const string svgMarkup = """
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40">
<switch>
<rect width="40" height="40" fill="#00ff00" systemLanguage="en-US" />
<rect width="40" height="40" fill="#ff0000" />
</switch>
</svg>
""";

using var source = SvgSource.LoadFromSvg(svgMarkup);
using var bitmap = RenderBitmap(source.Svg!, 40, 40);

AssertMostlyGreen(bitmap.GetPixel(20, 20));
}

private static void AssertInvariantGlobalization()
{
Assert.Throws<CultureNotFoundException>(() => CultureInfo.GetCultureInfo("en-US"));
}

private static SKBitmap RenderBitmap(global::Svg.Skia.SKSvg svg, int width, int height)
{
var picture = svg.Picture;
Assert.NotNull(picture);

var bitmap = new SKBitmap(new SKImageInfo(width, height, SKColorType.Rgba8888, SKAlphaType.Premul));
using var canvas = new SKCanvas(bitmap);
canvas.Clear(SKColors.Transparent);
canvas.DrawPicture(picture);
return bitmap;
}

private static void AssertMostlyGreen(SKColor color)
{
Assert.True(
color.Green > 180 && color.Red < 80 && color.Blue < 80 && color.Alpha > 220,
$"Expected mostly green, got {color}.");
}
}
Loading