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
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,30 @@ using (var svg = new SKSvg())
}
```

#### Load Android VectorDrawable

```C#
using Svg.Skia;

using (var svg = new SKSvg())
{
if (svg.LoadVectorDrawable("icon.xml") is { })
{
svg.Save("icon.png", SkiaSharp.SKColors.Transparent);
}
}
```

```C#
using Svg.Model.Services;

var document = SvgService.OpenVectorDrawable("icon.xml");
document?.Write("icon.svg");
```

When loading from a file path, `SKSvg.Load("icon.xml")` also auto-detects
Android `VectorDrawable` XML and routes it through the VectorDrawable importer.

### Hit Testing

#### SKSvg
Expand Down
2 changes: 1 addition & 1 deletion samples/Svg.Skia.Converter/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ private static async Task<int> Main(string[] args)

var rootCommand = new RootCommand
{
Description = "Converts a svg file to an encoded bitmap image."
Description = "Converts SVG and Android VectorDrawable files to SVG or encoded bitmap output."
};

rootCommand.AddOption(optionInputFiles);
Expand Down
64 changes: 62 additions & 2 deletions samples/Svg.Skia.Converter/SvgConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Xml;
using Newtonsoft.Json;
using Svg.Model;
using Svg.Model.Services;

namespace Svg.Skia.Converter;

internal class SvgConverter
{
private const string AndroidNamespace = "http://schemas.android.com/apk/res/android";

public static void Log(string message)
{
Console.WriteLine(message);
Expand Down Expand Up @@ -36,13 +40,68 @@ public static void GetFiles(System.IO.DirectoryInfo directory, string pattern, L
}
}

private static void GetVectorDrawableFiles(System.IO.DirectoryInfo directory, List<System.IO.FileInfo> paths)
{
var files = System.IO.Directory.EnumerateFiles(directory.FullName, "*.xml");
foreach (var path in files)
{
var fileInfo = new System.IO.FileInfo(path);
if (IsVectorDrawableFile(fileInfo))
{
paths.Add(fileInfo);
}
}
}

private static bool IsVectorDrawableFile(System.IO.FileInfo file)
{
var settings = new XmlReaderSettings
{
DtdProcessing = DtdProcessing.Prohibit,
IgnoreComments = true,
IgnoreProcessingInstructions = true
};

try
{
using var stream = System.IO.File.OpenRead(file.FullName);
using var reader = XmlReader.Create(stream, settings);
return reader.MoveToContent() == XmlNodeType.Element
&& string.Equals(reader.LocalName, "vector", StringComparison.Ordinal)
&& string.Equals(reader.LookupNamespace("android"), AndroidNamespace, StringComparison.Ordinal);
}
catch
{
return false;
}
}

public static bool Save(System.IO.FileInfo inputPath, string outputPath, string format, int quality, string background, float scale, float scaleX, float scaleY, bool quiet, int i)
{
if (quiet == false)
{
Log($"[{i}] File: {inputPath}");
}

if (string.Compare(format, "svg", StringComparison.OrdinalIgnoreCase) == 0)
{
var svgDocument = SvgService.Open(inputPath.FullName);
if (svgDocument is null)
{
Log($"Error: Failed to load input file: {inputPath.FullName}");
return false;
}

svgDocument.Write(outputPath);

if (quiet == false)
{
Log($"[{i}] Success: {outputPath}");
}

return true;
}

using var svg = new SKSvg();

if (svg.Load(inputPath.FullName) == null)
Expand Down Expand Up @@ -141,6 +200,7 @@ public static void Run(Settings settings)
{
GetFiles(directory, "*.svg", paths);
GetFiles(directory, "*.svgz", paths);
GetVectorDrawableFiles(directory, paths);
}
else
{
Expand Down Expand Up @@ -236,7 +296,7 @@ public static void Execute(System.IO.FileInfo? loadConfig, System.IO.FileInfo? s
{
var jsonSerializerSettings = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
Formatting = Newtonsoft.Json.Formatting.Indented,
NullValueHandling = NullValueHandling.Ignore,
Converters =
{
Expand Down Expand Up @@ -267,7 +327,7 @@ public static void Execute(System.IO.FileInfo? loadConfig, System.IO.FileInfo? s
{
var jsonSerializerSettings = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
Formatting = Newtonsoft.Json.Formatting.Indented,
NullValueHandling = NullValueHandling.Ignore,
Converters =
{
Expand Down
45 changes: 45 additions & 0 deletions src/Svg.Model/Services/SvgService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -685,10 +685,41 @@ public static SKSize GetDimensions(SvgFragment svgFragment, SKRect skViewport =
{
".svg" => OpenSvg(path, parameters),
".svgz" => OpenSvgz(path, parameters),
".xml" => IsVectorDrawablePath(path) ? OpenVectorDrawable(path, parameters) : OpenSvg(path, parameters),
_ => OpenSvg(path, parameters),
};
}

public static SvgDocument? OpenVectorDrawable(string path, SvgParameters? parameters = null)
{
_ = parameters;

using var fileStream = System.IO.File.OpenRead(path);
var svgDocument = OpenVectorDrawable(fileStream);
if (svgDocument is { })
{
svgDocument.BaseUri = new Uri(System.IO.Path.GetFullPath(path));
}

return svgDocument;
}

public static SvgDocument? OpenVectorDrawable(System.IO.Stream stream, SvgParameters? parameters = null)
{
_ = parameters;
return VectorDrawableConverter.Open(stream);
}

public static SvgDocument? FromVectorDrawable(string xml)
{
return VectorDrawableConverter.FromXml(xml);
}

public static SvgDocument? OpenVectorDrawable(XmlReader reader)
{
return VectorDrawableConverter.Open(reader);
}

public static SvgDocument? Open(System.IO.Stream stream, SvgParameters? parameters = null)
{
return SvgDocument.Open<SvgDocument>(stream, new SvgOptions(parameters?.Entities, parameters?.Css));
Expand All @@ -703,4 +734,18 @@ public static SKSize GetDimensions(SvgFragment svgFragment, SKRect skViewport =
{
return SvgDocument.Open<SvgDocument>(reader);
}

private static bool IsVectorDrawablePath(string path)
{
var settings = new XmlReaderSettings
{
DtdProcessing = DtdProcessing.Prohibit,
IgnoreComments = true,
IgnoreProcessingInstructions = true
};

using var fileStream = System.IO.File.OpenRead(path);
using var xmlReader = XmlReader.Create(fileStream, settings);
return VectorDrawableConverter.IsVectorDrawable(xmlReader);
}
}
Loading
Loading