From a386faa7f1e9727a49f1e26bd3e8aa9b74a90f94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Mon, 15 Jun 2026 12:21:18 +0200 Subject: [PATCH 1/2] Avoid invariant culture startup failure --- src/Svg.Model/Services/SvgService.cs | 29 +++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/Svg.Model/Services/SvgService.cs b/src/Svg.Model/Services/SvgService.cs index 017adc6417..09e66548b0 100644 --- a/src/Svg.Model/Services/SvgService.cs +++ b/src/Svg.Model/Services/SvgService.cs @@ -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' }; @@ -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; @@ -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(); From 62021fef6537a71ae962f1d06f7df30942adc517 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Mon, 15 Jun 2026 12:21:32 +0200 Subject: [PATCH 2/2] Cover SvgSource invariant globalization --- Svg.Skia.slnx | 1 + ...ia.InvariantGlobalization.UnitTests.csproj | 25 +++++ .../SvgSourceInvariantGlobalizationTests.cs | 92 +++++++++++++++++++ 3 files changed, 118 insertions(+) create mode 100644 tests/Svg.Controls.Skia.Avalonia.InvariantGlobalization.UnitTests/Svg.Controls.Skia.Avalonia.InvariantGlobalization.UnitTests.csproj create mode 100644 tests/Svg.Controls.Skia.Avalonia.InvariantGlobalization.UnitTests/SvgSourceInvariantGlobalizationTests.cs diff --git a/Svg.Skia.slnx b/Svg.Skia.slnx index c65fe404ee..f416c51414 100644 --- a/Svg.Skia.slnx +++ b/Svg.Skia.slnx @@ -106,6 +106,7 @@ + diff --git a/tests/Svg.Controls.Skia.Avalonia.InvariantGlobalization.UnitTests/Svg.Controls.Skia.Avalonia.InvariantGlobalization.UnitTests.csproj b/tests/Svg.Controls.Skia.Avalonia.InvariantGlobalization.UnitTests/Svg.Controls.Skia.Avalonia.InvariantGlobalization.UnitTests.csproj new file mode 100644 index 0000000000..acf14d4d61 --- /dev/null +++ b/tests/Svg.Controls.Skia.Avalonia.InvariantGlobalization.UnitTests/Svg.Controls.Skia.Avalonia.InvariantGlobalization.UnitTests.csproj @@ -0,0 +1,25 @@ + + + + net10.0 + Exe + False + true + enable + Avalonia.Svg.Skia.InvariantGlobalization.UnitTests + true + + + + + + + + + + + + + + + diff --git a/tests/Svg.Controls.Skia.Avalonia.InvariantGlobalization.UnitTests/SvgSourceInvariantGlobalizationTests.cs b/tests/Svg.Controls.Skia.Avalonia.InvariantGlobalization.UnitTests/SvgSourceInvariantGlobalizationTests.cs new file mode 100644 index 0000000000..4f6d8e6c7e --- /dev/null +++ b/tests/Svg.Controls.Skia.Avalonia.InvariantGlobalization.UnitTests/SvgSourceInvariantGlobalizationTests.cs @@ -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 = """ + + + + """; + + [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 = """ + + + + + + + """; + + 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(() => 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}."); + } +}