Resource SVG Converter - ResourceSvgConverter
The ResourceSvgConverter converter class takes multiple files and directories as input and outputs a ResourceDictionary in XAML format. This is useful for vector icons, but required supports in the rendering process to track brushes and pens, and an extension to the XmlXamlWriter class to format a compact XAML output. It is the most recent SharpVector SVG converter and adds extension features that will be supported in the other converters.
Note
Currently, the input is restricted to local or network files and directories.
The output is string or local or network file or one of the following
- Stream: A stream object created by the user.
- TextWriter: A text writer object created by the user.
- XmlWriter: An XML writer object created by the user
Resource Options
This converter offers extra settings or options in the form of WpfResourceSettings class.
The following are the properties exposed by the resource options to customize the output resource dictionary XAML:
- ResourceMode: An enumeration specifying the type of the resource object;
DrawingGroup or DrawingImage.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:po="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="po"> <DrawingGroup x:Key="shapes-rect-01-t" po:Freeze="True" ClipGeometry="F0M0,0L480,0L480,360L0,360z"> </DrawingGroup> </ResourceDictionary>
- ResourceFreeze: Makes the freezable resource object unmodifiable and sets its IsFrozen property to
true
. To freeze a resource object declared in markup, thePresentationOptions:Freeze
attribute is used, as shown above aspo.Freeze
, and thexmlns:po="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"
namespace is added.Note
If this property is set to
true
, it is admisable to set theResourceAccessType
type below toStatic
. - IndentSpaces: This specifies the number of spaces used for the indentation of the XAML output. It is honored with the XmlReaderSettings. The default is
2 spaces
. - NumericPrecision: This specifies numeric precision or number of decimal places for the floating number. The default is
4
, in complaince to the SVG specifications. Setting this to-1
will disable this property. - ColorNameFormat: This determines the key name of color objects, if both BindToResources and BindToColors properties are enabled. The default is
Color{0}
. - BrushNameFormat: This determines the key name of brush objects, if the BindToResources property is enabled. The default is
Brush{0}
.Note
Only solid color brushes are supported and extracted from the drawings.
- PenNameFormat: This determines the key name of pen or stroke objects, if the BindToResources property is enabled. The default is
Pen{0}
- BindToResources: This determines whether the media basic objects; color, brush and stroke/pen are extracted and the drawing objects bind to them. The default is
true
.<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Color x:Key="Color1">#FF000000</Color> <Color x:Key="Color2">#FF008000</Color> <SolidColorBrush x:Key="Brush1" Color="{DynamicResource Color1}" /> <SolidColorBrush x:Key="Brush2" Color="{DynamicResource Color2}" /> <Pen x:Key="Pen1" Brush="{DynamicResource Brush2}" Thickness="1" StartLineCap="Flat" EndLineCap="Flat" LineJoin="Miter" /> <DrawingImage x:Key="simple-path"> <DrawingImage.Drawing> <DrawingGroup ClipGeometry="F0M0,0L110,0L110,110L0,110z"> <GeometryDrawing Brush="{DynamicResource Brush1}" Pen="{DynamicResource Pen1}" Geometry="F1M10,30A20,20,0,0,1,50,30A20,20,0,0,1,90,30Q90,60 50,90 10,60 10,30z" /> </DrawingGroup> </DrawingImage.Drawing> </DrawingImage> </ResourceDictionary>
- BindToColors: This determines if the color object of brushes is extracted. The default is
true
.<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Color x:Key="Color1">#FF000000</Color> <Color x:Key="Color2">#FF008000</Color> <SolidColorBrush x:Key="Brush1" Color="{DynamicResource Color1}" /> <SolidColorBrush x:Key="Brush2" Color="{DynamicResource Color2}" /> <Pen x:Key="Pen1" Brush="{DynamicResource Brush2}" Thickness="1" StartLineCap="Flat" EndLineCap="Flat" LineJoin="Miter" /> <DrawingImage x:Key="simple-path"> <DrawingImage.Drawing> <DrawingGroup ClipGeometry="F0M0,0L110,0L110,110L0,110z"> <GeometryDrawing Brush="{DynamicResource Brush1}" Pen="{DynamicResource Pen1}" Geometry="F1M10,30A20,20,0,0,1,50,30A20,20,0,0,1,90,30Q90,60 50,90 10,60 10,30z" /> </DrawingGroup> </DrawingImage.Drawing> </DrawingImage> </ResourceDictionary>
- BindPenToBrushes: This determines if solid color brushes of pen/stroke is extracted. The default is
true
.<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Color x:Key="Color1">#FF000000</Color> <Color x:Key="Color2">#FF008000</Color> <SolidColorBrush x:Key="Brush1" Color="{DynamicResource Color1}" /> <SolidColorBrush x:Key="Brush2" Color="{DynamicResource Color2}" /> <Pen x:Key="Pen1" Brush="{DynamicResource Brush2}" Thickness="1" StartLineCap="Flat" EndLineCap="Flat" LineJoin="Miter" /> <DrawingImage x:Key="simple-path"> <DrawingImage.Drawing> <DrawingGroup ClipGeometry="F0M0,0L110,0L110,110L0,110z"> <GeometryDrawing Brush="{DynamicResource Brush1}" Pen="{DynamicResource Pen1}" Geometry="F1M10,30A20,20,0,0,1,50,30A20,20,0,0,1,90,30Q90,60 50,90 10,60 10,30z" /> </DrawingGroup> </DrawingImage.Drawing> </DrawingImage> </ResourceDictionary>
Note
If BindToResources property is set to
false
, then BindToColors and BindPenToBrushes values have no effect. - UseResourceIndex: This specifies whether a zero-based numbering is applied to the key names. The default is
false
.<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Color x:Key="Color0">#FF000000</Color> <Color x:Key="Color1">#FF008000</Color> <SolidColorBrush x:Key="Brush0" Color="{DynamicResource Color0}" /> <SolidColorBrush x:Key="Brush1" Color="{DynamicResource Color1}" /> <Pen x:Key="Pen0" Brush="{DynamicResource Brush1}" Thickness="1" StartLineCap="Flat" EndLineCap="Flat" LineJoin="Miter" /> <DrawingImage x:Key="simple-path"> <DrawingImage.Drawing> <DrawingGroup ClipGeometry="F0M0,0L110,0L110,110L0,110z"> <GeometryDrawing Brush="{DynamicResource Brush0}" Pen="{DynamicResource Pen0}" Geometry="F1M10,30A20,20,0,0,1,50,30A20,20,0,0,1,90,30Q90,60 50,90 10,60 10,30z" /> </DrawingGroup> </DrawingImage.Drawing> </DrawingImage> </ResourceDictionary>
- ColorPalette: This is a dictionary of color to resource key, specifying a predefined resource keys for the colors.
// Create the resource settings or options var resourceSettings = new WpfResourceSettings() { ColorPalette = new Dictionary<Color, string>(WpfDrawingResources.ColorComparer) { { (Color)ColorConverter.ConvertFromString("#FF000000"), "IconFill" }, { (Color)ColorConverter.ConvertFromString("#FF008000"), "IconBorder" }, } }; // Add a directory as SVG source resourceSettings.AddSource(svgDir); // Create the resource converter var converter = new ResourceSvgConverter(resourceSettings); // Perform the conversion to ResourceDictionary XAML var xamlText = converter.Convert();
- Sources: Gets an enumeration of all the SVG sources to be converted to the resource dictionary.
- SourceCount: Gets the count of the SVG sources to be converted to the resource dictionary.
- ResourceAccess: This specifies the type of the resource access, DynamicResource or StaticResource, to be applied to the resource objects.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Color x:Key="Color1">#FF000000</Color> <Color x:Key="Color2">#FF008000</Color> <SolidColorBrush x:Key="Brush1" Color="{DynamicResource Color1}" /> <SolidColorBrush x:Key="Brush2" Color="{DynamicResource Color2}" /> <Pen x:Key="Pen1" Brush="{DynamicResource Brush2}" Thickness="1" StartLineCap="Flat" EndLineCap="Flat" LineJoin="Miter" /> <DrawingImage x:Key="simple-path"> <DrawingImage.Drawing> <DrawingGroup ClipGeometry="F0M0,0L110,0L110,110L0,110z"> <GeometryDrawing Brush="{DynamicResource Brush1}" Pen="{DynamicResource Pen1}" Geometry="F1M10,30A20,20,0,0,1,50,30A20,20,0,0,1,90,30Q90,60 50,90 10,60 10,30z" /> </DrawingGroup> </DrawingImage.Drawing> </DrawingImage> </ResourceDictionary>
- ResourceResolverType: Get a value specifying the current type of the resource object key resolver, which is implemented through the IResourceKeyResolver interface. This applies to the main resource objects; DrawingGroup or DrawingImage (and not the color, brush or pen objects). It returns None, if no resource key resolver is attached, in which case the default resolver will be used.
ResourceKeyResolverType - Default
For the illustration of the resource key resolvers, we will assume a directory containing the following three SVG files
about.svg | area_chart.svg | crystal_oscillator.svg |
The following sample code uses the default resource key resolver, or ResourceKeyResolver, to resolve the keys. If not specified, the keys are simply the SVG file names without the file extension.
using System.Windows;
using System.Windows.Media;
using SharpVectors.Renderers;
using SharpVectors.Converters;
using SharpVectors.Renderers.Wpf;
namespace SharpVectors.Test.Sample
{
public class ResourceSvgConverterSample
{
private WpfResourceSettings _resourceSettings;
public ResourceSvgConverterSample()
{
// Create the resource settings or options
_resourceSettings = new WpfResourceSettings();
_resourceSettings.ResourceFreeze = false; // Do not freeze
}
public string Convert(string svgDir)
{
// Add a directory as SVG source
_resourceSettings.AddSource(svgDir);
// Create the resource converter
var converter = new ResourceSvgConverter(_resourceSettings);
// Perform the conversion to ResourceDictionary XAML
return converter.Convert();
}
}
}
The default resource key resolver supports simple string substitution template to customize the resource keys with the following two tags
- ${name}: Representing the SVG file name without the extension.
- ${number}: Representing the resource number or index, depending on the value of the UseResourceIndex property.
For instance, using a key format of icon_${name}, an SVG file with the name about.svg
will be have a resource key: icon_about.
The following sample code illustrates how the customized resource key format is applied with the default resolver, and the resulting XAML output:
using System.Windows;
using System.Windows.Media;
using SharpVectors.Renderers;
using SharpVectors.Converters;
using SharpVectors.Renderers.Wpf;
namespace SharpVectors.Test.Sample
{
public class ResourceSvgConverterSample
{
private WpfResourceSettings _resourceSettings;
public ResourceSvgConverterSample()
{
// Create the resource settings or options
_resourceSettings = new WpfResourceSettings();
_resourceSettings.ResourceFreeze = false; // Do not freeze
// Initialize the default key resolver and register it
var resolver = new ResourceKeyResolver("icon_${name}");
_resourceSettings.RegisterResolver(resolver);
}
public string Convert(string svgDir)
{
// Add a directory as SVG source
_resourceSettings.AddSource(svgDir);
// Create the resource converter
var converter = new ResourceSvgConverter(_resourceSettings);
// Perform the conversion to ResourceDictionary XAML
return converter.Convert();
}
}
}
ResourceKeyResolverType - Dictionary
For the cases where the resource keys are already defined, you can use the dictionary resolver, or DictionaryKeyResolver, which maps the SVG file names without the extension to the predefined keys.
The following sample code uses the dictionary resource key resolver, or DictionaryKeyResolver, to resolve the keys of the previous SVG sample files.
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
using SharpVectors.Renderers;
using SharpVectors.Converters;
using SharpVectors.Renderers.Wpf;
namespace SharpVectors.Test.Sample
{
public class ResourceSvgConverterSample
{
private WpfResourceSettings _resourceSettings;
public ResourceSvgConverterSample()
{
// Create the resource settings or options
_resourceSettings = new WpfResourceSettings();
_resourceSettings.ResourceFreeze = false; // Do not freeze
// Create a dictionary of SVG file name to predefined names
var dictionaryKeys = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{ "about", "About" },
{ "area_chart", "AreaChart" },
{ "crystal_oscillator", "CrystalOscillator" }
};
// Initialize the dictionary key resolver and register it
var resolver = new DictionaryKeyResolver(dictionaryKeys);
_resourceSettings.RegisterResolver(resolver);
}
public string Convert(string svgDir)
{
// Add a directory as SVG source
_resourceSettings.AddSource(svgDir);
// Create the resource converter
var converter = new ResourceSvgConverter(_resourceSettings);
// Perform the conversion to ResourceDictionary XAML
return converter.Convert();
}
}
}
ResourceKeyResolverType - CodeSnippet
In applications you need to use a code snippet to implement the IResourceKeyResolver interface, we provider the code snippet resolver, or CodeSnippetKeyResolver, which uses CodeDomProvider to compile the implementation in memory and use it to resolve the keys. This is used to reduce dependencies but limits the lanagauge feature supports to .NET 4.0.
For the code snippet, the following conditions are required
- The namespace must be
SharpVectors.Renderers
. - The class name must be
SnippetKeyResolver
.
The following sample code uses the snippet resource key resolver, or CodeSnippetKeyResolver, to resolve the keys. To reduce the code length for the screen, we will assume the snippet is stored in a file, and load it from the file.
using System;
using System.Xml;
using System.Windows;
namespace SharpVectors.Renderers {
public sealed class SnippetKeyResolver : WpfSettings<SnippetKeyResolver>, IResourceKeyResolver {
public SnippetKeyResolver() {
}
public ResourceKeyResolverType ResolverType {
get {
return ResourceKeyResolverType.Custom;
}
}
public bool IsValid {
get {
return true;
}
}
public void BeginResolve() {
}
public void EndResolve() {
}
public override SnippetKeyResolver Clone() {
return new SnippetKeyResolver();
}
public override void ReadXml(XmlReader reader) {
}
public override void WriteXml(XmlWriter writer) {
}
public string Resolve(DependencyObject resource, int index, string fileName, string fileSource) {
if (index < 0) {
throw new ArgumentException("The specified index is invalid", "index");
}
NotNullNotEmpty(fileName, "fileName");
var keyValue = ToLowerCamelCase(fileName.ToUpper());
if (!string.IsNullOrWhiteSpace(keyValue) && keyValue.Length >= 3 && keyValue.Length < 255) {
return keyValue;
}
return fileName;
}
private static string ToLowerCamelCase(string fileName) {
if (string.IsNullOrWhiteSpace(fileName)) {
return string.Empty;
}
string camelCaseStr = char.ToLower(fileName[0]).ToString();
if (fileName.Length > 1) {
bool isStartOfWord = false;
for (int i = 1; i < fileName.Length; i++) {
char currChar = fileName[i];
if (currChar == '_' || currChar == '-') {
isStartOfWord = true;
} else if (char.IsUpper(currChar)) {
if (isStartOfWord) {
camelCaseStr += currChar;
} else {
camelCaseStr += char.ToLower(currChar);
}
isStartOfWord = false;
} else {
camelCaseStr += currChar;
isStartOfWord = false;
}
}
}
return camelCaseStr;
}
}
}
The test code snippet (C# code) will convert the specified SVG file name to the lower camel case naming format as the resource key.
using System.Windows;
using System.Windows.Media;
using SharpVectors.Renderers;
using SharpVectors.Converters;
using SharpVectors.Renderers.Wpf;
namespace SharpVectors.Test.Sample
{
public class ResourceSvgConverterSample
{
private WpfResourceSettings _resourceSettings;
public ResourceSvgConverterSample(string snippetFile)
{
// Create the resource settings or options
_resourceSettings = new WpfResourceSettings();
_resourceSettings.ResourceFreeze = false; // Do not freeze
// Get the code snippet, here assumed from a file
string codeSnippet = string.Empty;
using (var reader = new System.IO.StreamReader(snippetFile))
{
codeSnippet = reader.ReadToEnd();
}
// Initialize the code snippet key resolver and register it
var resolver = new CodeSnippetKeyResolver(codeSnippet, "cs");
_resourceSettings.RegisterResolver(resolver);
}
public string Convert(string svgDir)
{
// Add a directory as SVG source
_resourceSettings.AddSource(svgDir);
// Create the resource converter
var converter = new ResourceSvgConverter(_resourceSettings);
// Perform the conversion to ResourceDictionary XAML
return converter.Convert();
}
}
}
ResourceKeyResolverType - Custom
You can create a custom resource key resolver by implementing the IResourceKeyResolver interface.
The following sample code implements a resource key resolver that will convert the specified SVG file name to the upper camel case naming format as the resource key.
using System;
using System.Xml;
using System.Windows;
using SharpVectors.Renderers;
namespace SharpVectors.Test.Sample {
internal sealed class CustomResourceKeyResolver : WpfSettings<CustomResourceKeyResolver>, IResourceKeyResolver {
public CustomResourceKeyResolver() {
}
public override CustomResourceKeyResolver Clone() {
return new CustomResourceKeyResolver();
}
public override void ReadXml(XmlReader reader) {
NotNull(reader, nameof(reader));
}
public override void WriteXml(XmlWriter writer) {
NotNull(writer, nameof(writer));
}
public ResourceKeyResolverType ResolverType {
get {
return ResourceKeyResolverType.Custom;
}
}
public bool IsValid {
get {
return true;
}
}
public void BeginResolve() {
}
public void EndResolve() {
}
public string Resolve(DependencyObject resource, int index, string fileName, string fileSource) {
if (index < 0) {
throw new ArgumentException("The specified index is invalid", "index");
}
NotNullNotEmpty(fileName, "fileName");
var keyValue = ToUpperCamelCase(fileName.ToUpper());
if (!string.IsNullOrWhiteSpace(keyValue) && keyValue.Length >= 3 && keyValue.Length < 255) {
return keyValue;
}
return fileName;
}
internal static string ToUpperCamelCase(string fileName) {
if (string.IsNullOrWhiteSpace(fileName)) {
return string.Empty;
}
string camelCaseStr = fileName[0].ToString();
if (fileName.Length > 1) {
bool isStartOfWord = false;
for (int i = 1; i < fileName.Length; i++) {
char currChar = fileName[i];
if (currChar == '_' || currChar == '-') {
isStartOfWord = true;
} else if (char.IsUpper(currChar)) {
if (isStartOfWord) {
camelCaseStr += currChar;
} else {
camelCaseStr += char.ToLower(currChar);
}
isStartOfWord = false;
} else {
camelCaseStr += currChar;
isStartOfWord = false;
}
}
}
return camelCaseStr;
}
}
}
The following illustrate how to use the custom resource key resolver.
using System.Windows;
using System.Windows.Media;
using SharpVectors.Renderers;
using SharpVectors.Converters;
using SharpVectors.Renderers.Wpf;
namespace SharpVectors.Test.Sample
{
public class ResourceSvgConverterSample
{
private WpfResourceSettings _resourceSettings;
public ResourceSvgConverterSample()
{
// Create the resource settings or options
_resourceSettings = new WpfResourceSettings();
_resourceSettings.ResourceFreeze = false; // Do not freeze
// Initialize the custom key resolver and register it
var resolver = new CustomResourceKeyResolver();
_resourceSettings.RegisterResolver(resolver);
}
public string Convert(string svgDir)
{
// Add a directory as SVG source
_resourceSettings.AddSource(svgDir);
// Create the resource converter
var converter = new ResourceSvgConverter(_resourceSettings);
// Perform the conversion to ResourceDictionary XAML
return converter.Convert();
}
}
}
Options XML Serialization
The resource settings, WpfResourceSettings object can be saved to file in an XML format, or initialized from an XML format file.
You can, therefore, create a resource dictionary converter options for a project and share with other developers or projects. The following code defines a sample resource dictionary converter options and serializes WpfResourceSettings object to XML.
using System.Collections.Generic;
using System.Windows.Media;
using SharpVectors.Renderers;
using SharpVectors.Renderers.Wpf;
namespace SharpVectors.Test.Sample
{
public class ResourceSvgConverterSample
{
private WpfResourceSettings _resourceSettings;
public ResourceSvgConverterSample()
{
// Create the resource settings or options
_resourceSettings = new WpfResourceSettings();
_resourceSettings.ResourceFreeze = false; // Do not freeze
// Initialize the default key resolver and register it
var resolver = new ResourceKeyResolver("icon_${name}");
_resourceSettings.RegisterResolver(resolver);
// Add predefined color palette
_resourceSettings.ColorPalette = new Dictionary<Color, string>(WpfDrawingResources.ColorComparer)
{
{(Color)ColorConverter.ConvertFromString("#FF008000"), "SvgColor01"},
{(Color)ColorConverter.ConvertFromString("#FF000000"), "SvgColor02"},
{(Color)ColorConverter.ConvertFromString("#FFFFFF00"), "SvgColor03"},
{(Color)ColorConverter.ConvertFromString("#FF0000FF"), "SvgColor04"},
{(Color)ColorConverter.ConvertFromString("#FF00FF00"), "SvgColor05"},
{(Color)ColorConverter.ConvertFromString("#FF339966"), "SvgColor06"},
{(Color)ColorConverter.ConvertFromString("#FFFF00FF"), "SvgColor07"},
{(Color)ColorConverter.ConvertFromString("#FFFFA500"), "SvgColor08"},
{(Color)ColorConverter.ConvertFromString("#FF007700"), "SvgColor09"},
{(Color)ColorConverter.ConvertFromString("#FF33CC66"), "SvgColor10"}
};
// Add directories as SVG source
_resourceSettings.AddSource(@"C:\Abc-Project\Icons1");
_resourceSettings.AddSource(@"C:\Abc-Project\Icons2");
}
public string Save()
{
// Serialize the resource settings to XML
return _resourceSettings.Save();
}
}
}