Skip to content

Commit

Permalink
Add support for loading SvgSource with css support
Browse files Browse the repository at this point in the history
  • Loading branch information
wieslawsoltes committed Jan 27, 2024
1 parent df640e9 commit 80a53b3
Show file tree
Hide file tree
Showing 12 changed files with 222 additions and 101 deletions.
4 changes: 2 additions & 2 deletions build/Base.props
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VersionPrefix>1.0.0.12</VersionPrefix>
<VersionPrefix>1.0.0.13</VersionPrefix>
<VersionSuffix></VersionSuffix>
<AvaloniaVersionPrefix>11.0.0.12</AvaloniaVersionPrefix>
<AvaloniaVersionPrefix>11.0.0.13</AvaloniaVersionPrefix>
<AvaloniaVersionSuffix>$(VersionSuffix)</AvaloniaVersionSuffix>
<Authors>Wiesław Šoltés</Authors>
<Company>Wiesław Šoltés</Company>
Expand Down
6 changes: 6 additions & 0 deletions samples/AvaloniaSvgSkiaSample/App.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,10 @@
<Application.Styles>
<FluentTheme />
</Application.Styles>
<Application.Resources>
<SvgSource x:Key="CameraIcon">/Assets/__AJ_Digital_Camera.svg</SvgSource>
<SvgSource x:Key="TigerIcon"
Path="/Assets/__tiger.svg"
Css=".Black { fill: #FF0000; }" />
</Application.Resources>
</Application>
126 changes: 63 additions & 63 deletions samples/AvaloniaSvgSkiaSample/Assets/__tiger.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 28 additions & 0 deletions samples/AvaloniaSvgSkiaSample/MainWindow.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,34 @@
</DockPanel>
</TabItem>

<TabItem Header="Image">
<DockPanel Background="Transparent"
Margin="16"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
DragDrop.AllowDrop="True">
<Image>
<Image.Source>
<SvgImage Source="{DynamicResource CameraIcon}" />
</Image.Source>
</Image>
</DockPanel>
</TabItem>

<TabItem Header="Image Css">
<DockPanel Background="Transparent"
Margin="16"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
DragDrop.AllowDrop="True">
<Image>
<Image.Source>
<SvgImage Source="{DynamicResource TigerIcon}" />
</Image.Source>
</Image>
</DockPanel>
</TabItem>

<TabItem Header="Extension">
<DockPanel x:Name="svgExtensionDockPanel"
Background="Transparent"
Expand Down
9 changes: 5 additions & 4 deletions samples/AvaloniaSvgSkiaSample/MainWindow.axaml.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.IO;
using System;
using System.IO;
using System.Linq;
using Avalonia;
using Avalonia.Controls;
Expand Down Expand Up @@ -106,7 +107,7 @@ private void Drop(object sender, DragEventArgs e)
}
else if (sender == svgExtensionDockPanel)
{
var svg = new SvgSource();
var svg = new SvgSource(default(Uri));
var picture = svg.Load(fileName);
if (picture is { })
{
Expand All @@ -118,7 +119,7 @@ private void Drop(object sender, DragEventArgs e)
}
else if (sender == svgSourceDockPanel)
{
var svg = new SvgSource();
var svg = new SvgSource(default(Uri));
var picture = svg.Load(fileName);
if (picture is { })
{
Expand All @@ -130,7 +131,7 @@ private void Drop(object sender, DragEventArgs e)
}
else if (sender == svgResourceDockPanel)
{
var svg = new SvgSource();
var svg = new SvgSource(default(Uri));
var picture = svg.Load(fileName);
if (picture is { })
{
Expand Down
5 changes: 5 additions & 0 deletions src/Avalonia.Svg.Skia/IsExternalInit.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#if NET461 || NETSTANDARD
// ReSharper disable once CheckNamespace
namespace System.Runtime.CompilerServices;
internal static class IsExternalInit {}
#endif
4 changes: 2 additions & 2 deletions src/Avalonia.Svg.Skia/Svg.cs
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ private void LoadFromPath(string? path, SvgParameters? parameters = null)

try
{
_svg = SvgSource.Load<SvgSource>(path, _baseUri, parameters);
_svg = SvgSource.Load(path, _baseUri, parameters);

if (_enableCache && _cache is { } && _svg is { })
{
Expand All @@ -357,7 +357,7 @@ private void LoadFromSource(string? source)

try
{
_svg = SvgSource.LoadFromSvg<SvgSource>(source);
_svg = SvgSource.LoadFromSvg(source);
}
catch (Exception e)
{
Expand Down
4 changes: 2 additions & 2 deletions src/Avalonia.Svg.Skia/SvgImageExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ private static IImage CreateImage(string path, Uri baseUri, Control? targetContr
{
var css = Svg.GetCss(targetControl);
var currentCss = Svg.GetCurrentCss(targetControl);
var source = SvgSource.Load<SvgSource>(
var source = SvgSource.Load(
path,
baseUri,
new SvgParameters(null, string.Concat(css, ' ', currentCss)));
Expand All @@ -60,7 +60,7 @@ private static IImage CreateImage(string path, Uri baseUri, Control? targetContr
}
else
{
var source = SvgSource.Load<SvgSource>(
var source = SvgSource.Load(
path,
baseUri);

Expand Down
119 changes: 105 additions & 14 deletions src/Avalonia.Svg.Skia/SvgSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using System.Diagnostics;
using System.IO;
using System.Net.Http;
using Avalonia.Metadata;
using SkiaSharp;
using Svg;
using Svg.Model;
using Svg.Skia;
Expand All @@ -16,23 +18,112 @@ namespace Avalonia.Svg.Skia;
[TypeConverter(typeof(SvgSourceTypeConverter))]
public class SvgSource : SKSvg
{
private readonly IServiceProvider? _serviceProvider;
private readonly Uri? _baseUri;
private SKPicture? _picture;

[Content]
public string? Path { get; init; }

public Dictionary<string, string>? Entities { get; init; }

public string? Css { get; init; }

public override SKPicture? Picture
{
get
{
if (_picture is null && Path is not null)
{
_picture = LoadImpl(this, Path, _baseUri, new SvgParameters(Entities, Css));
}

return _picture;
}
protected set => _picture = value;
}

/// <summary>
/// Initializes a new instance of the <see cref="SvgSource"/> class.
/// </summary>
/// <param name="baseUri">The base URL for the XAML context.</param>
public SvgSource(Uri? baseUri)
{
_baseUri = baseUri;
}

/// <summary>
/// Initializes a new instance of the <see cref="SvgSource"/> class.
/// </summary>
/// <param name="serviceProvider">The XAML service provider.</param>
public SvgSource(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
_baseUri = serviceProvider.GetContextBaseUri();
}

/// <summary>
/// Enable throw exception on missing resource.
/// </summary>
public static bool EnableThrowOnMissingResource { get; set; }

private static SKPicture? LoadImpl(SvgSource source, string path, Uri? baseUri, SvgParameters? parameters = null)
{
if (File.Exists(path))
{
return source.Load(path, parameters);
}

if (Uri.TryCreate(path, UriKind.Absolute, out var uriHttp) && (uriHttp.Scheme == "http" || uriHttp.Scheme == "https"))
{
try
{
var response = new HttpClient().GetAsync(uriHttp).Result;
if (response.IsSuccessStatusCode)
{
var stream = response.Content.ReadAsStreamAsync().Result;
return source.Load(stream, parameters);
}
}
catch (HttpRequestException e)
{
Debug.WriteLine("Failed to connect to " + uriHttp);
Debug.WriteLine(e.ToString());
}

ThrowOnMissingResource(path);
return null;
}

var uri = path.StartsWith("/") ? new Uri(path, UriKind.Relative) : new Uri(path, UriKind.RelativeOrAbsolute);
if (uri.IsAbsoluteUri && uri.IsFile)
{
return source.Load(uri.LocalPath, parameters);
}
else
{
var stream = Platform.AssetLoader.Open(uri, baseUri);
if (stream is null)
{
ThrowOnMissingResource(path);
return null;
}
return source.Load(stream, parameters);
}
}

/// <summary>t
/// Loads svg source from file or resource.
/// </summary>
/// <param name="path">The path to file or resource.</param>
/// <param name="baseUri">The base uri.</param>
/// <param name="parameters">The svg parameters.</param>
/// <returns>The svg source.</returns>
public static T? Load<T>(string path, Uri? baseUri, SvgParameters? parameters = null) where T : SKSvg, new()
public static SvgSource? Load(string path, Uri? baseUri, SvgParameters? parameters = null)
{
if (File.Exists(path))
{
var source = new T();
var source = new SvgSource(baseUri);
source.Load(path, parameters);
return source;
}
Expand All @@ -45,7 +136,7 @@ public class SvgSource : SKSvg
if (response.IsSuccessStatusCode)
{
var stream = response.Content.ReadAsStreamAsync().Result;
var source = new T();
var source = new SvgSource(baseUri);
source.Load(stream, parameters);
return source;
}
Expand All @@ -56,13 +147,13 @@ public class SvgSource : SKSvg
Debug.WriteLine(e.ToString());
}

return ThrowOnMissingResource<T>(path);
return ThrowOnMissingResource(path);
}

var uri = path.StartsWith("/") ? new Uri(path, UriKind.Relative) : new Uri(path, UriKind.RelativeOrAbsolute);
if (uri.IsAbsoluteUri && uri.IsFile)
{
var source = new T();
var source = new SvgSource(baseUri);
source.Load(uri.LocalPath, parameters);
return source;
}
Expand All @@ -71,9 +162,9 @@ public class SvgSource : SKSvg
var stream = Platform.AssetLoader.Open(uri, baseUri);
if (stream is null)
{
return ThrowOnMissingResource<T>(path);
return ThrowOnMissingResource(path);
}
var source = new T();
var source = new SvgSource(baseUri);
source.Load(stream, parameters);
return source;
}
Expand All @@ -84,9 +175,9 @@ public class SvgSource : SKSvg
/// </summary>
/// <param name="source">The svg source.</param>
/// <returns>The svg source.</returns>
public static T? LoadFromSvg<T>(string source) where T : SKSvg, new()
public static SvgSource? LoadFromSvg(string source)
{
var skSvg = new T();
var skSvg = new SvgSource(default(Uri));
skSvg.FromSvg(source);
return skSvg;
}
Expand All @@ -97,9 +188,9 @@ public class SvgSource : SKSvg
/// <param name="stream">The svg stream.</param>
/// <param name="parameters">The svg parameters.</param>
/// <returns>The svg source.</returns>
public static T? LoadFromStream<T>(Stream stream, SvgParameters? parameters = null) where T : SKSvg, new()
public static SvgSource? LoadFromStream(Stream stream, SvgParameters? parameters = null)
{
var skSvg = new T();
var skSvg = new SvgSource(default(Uri));
skSvg.Load(stream, parameters);
return skSvg;
}
Expand All @@ -109,14 +200,14 @@ public class SvgSource : SKSvg
/// </summary>
/// <param name="document">The svg document.</param>
/// <returns>The svg source.</returns>
public static T? LoadFromSvgDocument<T>(SvgDocument document) where T : SKSvg, new()
public static SvgSource? LoadFromSvgDocument(SvgDocument document)
{
var skSvg = new T();
var skSvg = new SvgSource(default(Uri));
skSvg.FromSvgDocument(document);
return skSvg;
}

private static T? ThrowOnMissingResource<T>(string path) where T : SKSvg, new()
private static SvgSource? ThrowOnMissingResource(string path)
{
return EnableThrowOnMissingResource
? throw new ArgumentException($"Invalid resource path: {path}")
Expand Down
2 changes: 1 addition & 1 deletion src/Avalonia.Svg.Skia/SvgSourceTypeConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ public override bool CanConvertFrom(ITypeDescriptorContext? context, Type source
{
var path = (string)value;
var baseUri = context?.GetContextBaseUri();
return SvgSource.Load<SvgSource>(path, baseUri);
return SvgSource.Load(path, baseUri);
}
}
Loading

0 comments on commit 80a53b3

Please sign in to comment.