diff --git a/src/Avalonia.Svg.Skia/Svg.cs b/src/Avalonia.Svg.Skia/Svg.cs index 8888f0be..210599ea 100644 --- a/src/Avalonia.Svg.Skia/Svg.cs +++ b/src/Avalonia.Svg.Skia/Svg.cs @@ -25,6 +25,12 @@ public class Svg : Control public static readonly StyledProperty PathProperty = AvaloniaProperty.Register(nameof(Path)); + /// + /// Defines the property. + /// + public static readonly StyledProperty SourceProperty = + AvaloniaProperty.Register(nameof(Source)); + /// /// Defines the property. /// @@ -57,6 +63,15 @@ public string? Path set => SetValue(PathProperty, value); } + /// + /// Gets or sets the Svg source. + /// + public string? Source + { + get => GetValue(SourceProperty); + set => SetValue(SourceProperty, value); + } + /// /// Gets or sets a value controlling how the image will be stretched. /// @@ -101,8 +116,8 @@ public bool EnableCache static Svg() { - AffectsRender(PathProperty, StretchProperty, StretchDirectionProperty); - AffectsMeasure(PathProperty, StretchProperty, StretchDirectionProperty); + AffectsRender(PathProperty, SourceProperty, StretchProperty, StretchDirectionProperty); + AffectsMeasure(PathProperty, SourceProperty, StretchProperty, StretchDirectionProperty); } /// @@ -135,7 +150,6 @@ protected override Size MeasureOverride(Size availableSize) : default; return Stretch.CalculateSize(availableSize, sourceSize, StretchDirection); - } protected override Size ArrangeOverride(Size finalSize) @@ -201,7 +215,14 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang if (change.Property == PathProperty) { var path = change.GetNewValue(); - Load(path); + LoadFromPath(path); + InvalidateVisual(); + } + + if (change.Property == SourceProperty) + { + var source = change.GetNewValue(); + LoadFromSource(source); InvalidateVisual(); } @@ -219,7 +240,7 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang } } - private void Load(string? path) + private void LoadFromPath(string? path, Dictionary? entities = null) { if (path is null) { @@ -243,7 +264,7 @@ private void Load(string? path) try { - _svg = SvgSource.Load(path, _baseUri); + _svg = SvgSource.Load(path, _baseUri, entities); if (_enableCache && _cache is { } && _svg is { }) { @@ -257,6 +278,27 @@ private void Load(string? path) } } + private void LoadFromSource(string? source) + { + if (source is null) + { + _svg?.Dispose(); + _svg = null; + DisposeCache(); + return; + } + + try + { + _svg = SvgSource.LoadFromSvg(source); + } + catch (Exception e) + { + Logger.TryGet(LogEventLevel.Warning, LogArea.Control)?.Log(this, "Failed to load svg image: " + e); + _svg = null; + } + } + private void DisposeCache() { if (_cache is null) diff --git a/src/Avalonia.Svg.Skia/SvgImage.cs b/src/Avalonia.Svg.Skia/SvgImage.cs index 41504cb4..ad26666b 100644 --- a/src/Avalonia.Svg.Skia/SvgImage.cs +++ b/src/Avalonia.Svg.Skia/SvgImage.cs @@ -1,6 +1,4 @@ -using System; -using Avalonia.Media; -using Avalonia.Media.Imaging; +using Avalonia.Media; using Avalonia.Metadata; namespace Avalonia.Svg.Skia; diff --git a/src/Avalonia.Svg.Skia/SvgSource.cs b/src/Avalonia.Svg.Skia/SvgSource.cs index fc1aa039..2ac07b7c 100644 --- a/src/Avalonia.Svg.Skia/SvgSource.cs +++ b/src/Avalonia.Svg.Skia/SvgSource.cs @@ -1,8 +1,10 @@ using System; +using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Net.Http; +using Svg; using Svg.Skia; namespace Avalonia.Svg.Skia; @@ -18,13 +20,14 @@ public class SvgSource : SKSvg /// /// The path to file or resource. /// The base uri. + /// The svg entities. /// The svg source. - public static T? Load(string path, Uri? baseUri) where T : SKSvg, new() + public static T? Load(string path, Uri? baseUri, Dictionary? entities = null) where T : SKSvg, new() { if (File.Exists(path)) { var source = new T(); - source.Load(path); + source.Load(path, entities); return source; } @@ -37,7 +40,7 @@ public class SvgSource : SKSvg { var stream = response.Content.ReadAsStreamAsync().Result; var source = new T(); - source.Load(stream); + source.Load(stream, entities); return source; } } @@ -54,7 +57,7 @@ public class SvgSource : SKSvg if (uri.IsAbsoluteUri && uri.IsFile) { var source = new T(); - source.Load(uri.LocalPath); + source.Load(uri.LocalPath, entities); return source; } else @@ -65,8 +68,45 @@ public class SvgSource : SKSvg return default; } var source = new T(); - source.Load(stream); + source.Load(stream, entities); return source; } } + + /// t + /// Loads svg source from svg source. + /// + /// The svg source. + /// The svg source. + public static T? LoadFromSvg(string source) where T : SKSvg, new() + { + var skSvg = new T(); + skSvg.FromSvg(source); + return skSvg; + } + + /// t + /// Loads svg source from file or resource. + /// + /// The svg stream. + /// The svg entities. + /// The svg source. + public static T? LoadFromStream(Stream stream, Dictionary? entities = null) where T : SKSvg, new() + { + var skSvg = new T(); + skSvg.Load(stream, entities); + return skSvg; + } + + /// t + /// Loads svg source from svg document. + /// + /// The svg document. + /// The svg source. + public static T? LoadFromSvgDocument(SvgDocument document) where T : SKSvg, new() + { + var skSvg = new T(); + skSvg.FromSvgDocument(document); + return skSvg; + } } diff --git a/src/Svg.Model/SvgExtensions.IO.cs b/src/Svg.Model/SvgExtensions.IO.cs index 4d2284ad..75146e5e 100644 --- a/src/Svg.Model/SvgExtensions.IO.cs +++ b/src/Svg.Model/SvgExtensions.IO.cs @@ -4,6 +4,7 @@ using System.IO.Compression; using System.Net; using System.Text; +using System.Xml; using Svg.Model.Drawables; #if USE_SKIASHARP using SkiaSharp; @@ -296,12 +297,12 @@ internal static SvgDocument LoadSvgz(System.IO.Stream stream, Uri baseUri) return picture; } #endif - public static SvgDocument? OpenSvg(string path) + public static SvgDocument? OpenSvg(string path, Dictionary? entities = null) { - return SvgDocument.Open(path, null); + return SvgDocument.Open(path, entities); } - public static SvgDocument? OpenSvgz(string path) + public static SvgDocument? OpenSvgz(string path, Dictionary? entities = null) { using var fileStream = System.IO.File.OpenRead(path); using var gzipStream = new GZipStream(fileStream, CompressionMode.Decompress); @@ -310,27 +311,32 @@ internal static SvgDocument LoadSvgz(System.IO.Stream stream, Uri baseUri) gzipStream.CopyTo(memoryStream); memoryStream.Position = 0; - return Open(memoryStream); + return Open(memoryStream, entities); } - public static SvgDocument? Open(string path) + public static SvgDocument? Open(string path, Dictionary? entities = null) { var extension = System.IO.Path.GetExtension(path); return extension.ToLower() switch { - ".svg" => OpenSvg(path), - ".svgz" => OpenSvgz(path), - _ => OpenSvg(path), + ".svg" => OpenSvg(path, entities), + ".svgz" => OpenSvgz(path, entities), + _ => OpenSvg(path, entities), }; } - public static SvgDocument? Open(System.IO.Stream stream) + public static SvgDocument? Open(System.IO.Stream stream, Dictionary? entities = null) { - return SvgDocument.Open(stream, null); + return SvgDocument.Open(stream, entities); } public static SvgDocument? FromSvg(string svg) { return SvgDocument.FromSvg(svg); } + + public static SvgDocument? Open(XmlReader reader) + { + return SvgDocument.Open(reader); + } } diff --git a/src/Svg.Skia/SKSvg.Model.cs b/src/Svg.Skia/SKSvg.Model.cs index b2a8ce76..9fb6633b 100644 --- a/src/Svg.Skia/SKSvg.Model.cs +++ b/src/Svg.Skia/SKSvg.Model.cs @@ -2,6 +2,7 @@ using System; using Svg.Model; using System.Collections.Generic; +using System.Xml; using Svg.Model.Drawables; using ShimSkiaSharp; @@ -9,6 +10,41 @@ namespace Svg.Skia; public class SKSvg : IDisposable { + public static SKSvg CreateFromStream(System.IO.Stream stream, Dictionary? entities = null) + { + var skSvg = new SKSvg(); + skSvg.Load(stream, entities); + return skSvg; + } + + public static SKSvg CreateFromFile(string path, Dictionary? entities = null) + { + var skSvg = new SKSvg(); + skSvg.Load(path, entities); + return skSvg; + } + + public static SKSvg CreateFromXmlReader(XmlReader reader) + { + var skSvg = new SKSvg(); + skSvg.Load(reader); + return skSvg; + } + + public static SKSvg CreateFromSvg(string svg) + { + var skSvg = new SKSvg(); + skSvg.FromSvg(svg); + return skSvg; + } + + public static SKSvg CreateFromSvgDocument(SvgDocument svgDocument) + { + var skSvg = new SKSvg(); + skSvg.FromSvgDocument(svgDocument); + return skSvg; + } + public static SkiaSharp.SKPicture? ToPicture(SvgFragment svgFragment, SkiaModel skiaModel, IAssetLoader assetLoader) { var picture = SvgExtensions.ToModel(svgFragment, assetLoader, out _, out _); @@ -57,10 +93,10 @@ public SKSvg() AssetLoader = new SkiaAssetLoader(SkiaModel); } - public SkiaSharp.SKPicture? Load(System.IO.Stream stream) + public SkiaSharp.SKPicture? Load(System.IO.Stream stream, Dictionary? entities = null) { Reset(); - var svgDocument = SvgExtensions.Open(stream); + var svgDocument = SvgExtensions.Open(stream, entities); if (svgDocument is { }) { Model = SvgExtensions.ToModel(svgDocument, AssetLoader, out var drawable, out _); @@ -71,10 +107,24 @@ public SKSvg() return null; } - public SkiaSharp.SKPicture? Load(string path) + public SkiaSharp.SKPicture? Load(string path, Dictionary? entities = null) { Reset(); - var svgDocument = SvgExtensions.Open(path); + var svgDocument = SvgExtensions.Open(path, entities); + if (svgDocument is { }) + { + Model = SvgExtensions.ToModel(svgDocument, AssetLoader, out var drawable, out _); + Drawable = drawable; + Picture = SkiaModel.ToSKPicture(Model); + return Picture; + } + return null; + } + + public SkiaSharp.SKPicture? Load(XmlReader reader) + { + Reset(); + var svgDocument = SvgExtensions.Open(reader); if (svgDocument is { }) { Model = SvgExtensions.ToModel(svgDocument, AssetLoader, out var drawable, out _); diff --git a/src/Svg.Skia/SKSvg.NoModel.cs b/src/Svg.Skia/SKSvg.NoModel.cs index ef3e9d50..d3a8b83a 100644 --- a/src/Svg.Skia/SKSvg.NoModel.cs +++ b/src/Svg.Skia/SKSvg.NoModel.cs @@ -1,11 +1,48 @@ #if USE_SKIASHARP using System; +using System.Collections.Generic; +using System.Xml; using Svg.Model; namespace Svg.Skia; public class SKSvg : IDisposable { + public static SKSvg CreateFromStream(System.IO.Stream stream, Dictionary? entities = null) + { + var skSvg = new SKSvg(); + skSvg.Load(stream, entities); + return skSvg; + } + + public static SKSvg CreateFromFile(string path, Dictionary? entities = null) + { + var skSvg = new SKSvg(); + skSvg.Load(path, entities); + return skSvg; + } + + public static SKSvg CreateFromXmlReader(XmlReader reader) + { + var skSvg = new SKSvg(); + skSvg.Load(reader); + return skSvg; + } + + public static SKSvg CreateFromSvg(string svg) + { + var skSvg = new SKSvg(); + skSvg.FromSvg(svg); + return skSvg; + } + + public static SKSvg CreateFromSvgDocument(SvgDocument svgDocument) + { + var skSvg = new SKSvg(); + skSvg.FromSvgDocument(svgDocument); + return skSvg; + } + public static SkiaSharp.SKPicture? ToPicture(SvgFragment svgFragment, IAssetLoader assetLoader) { return SvgExtensions.ToPicture(svgFragment, assetLoader, out _, out _); @@ -23,10 +60,22 @@ public SKSvg() AssetLoader = new SkiaAssetLoader(); } - public SkiaSharp.SKPicture? Load(System.IO.Stream stream) + public SkiaSharp.SKPicture? Load(System.IO.Stream stream, Dictionary? entities = null) + { + Reset(); + var svgDocument = SvgExtensions.Open(stream, entities); + if (svgDocument is { }) + { + Picture = SvgExtensions.ToPicture(svgDocument, AssetLoader, out var _, out _); + return Picture; + } + return null; + } + + public SkiaSharp.SKPicture? Load(string path, Dictionary? entities = null) { Reset(); - var svgDocument = SvgExtensions.Open(stream); + var svgDocument = SvgExtensions.Open(path, entities); if (svgDocument is { }) { Picture = SvgExtensions.ToPicture(svgDocument, AssetLoader, out var _, out _); @@ -35,10 +84,10 @@ public SKSvg() return null; } - public SkiaSharp.SKPicture? Load(string path) + public SkiaSharp.SKPicture? Load(XmlReader reader) { Reset(); - var svgDocument = SvgExtensions.Open(path); + var svgDocument = SvgExtensions.Open(reader); if (svgDocument is { }) { Picture = SvgExtensions.ToPicture(svgDocument, AssetLoader, out var _, out _);