Skip to content

Commit

Permalink
CSS Support
Browse files Browse the repository at this point in the history
- Very initial support of CSS stylesheets in the SVG via including the
ExCss and Fizzler libraries
- Bug fixes with path rendering
- Improvements to the API for loading an SVG file
  • Loading branch information
erdomke committed Aug 1, 2014
1 parent 32ea4ec commit 3aedd8e
Show file tree
Hide file tree
Showing 96 changed files with 15,061 additions and 122 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ Source/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache
Samples/SVGViewer/obj/
Samples/SVGViewer/bin/
Samples/SVGViewer/SVGViewer.OpenCover.Settings
*.dll
*.pdb
40 changes: 40 additions & 0 deletions Source/Css/CssQuery.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Fizzler;
using ExCSS;

namespace Svg.Css
{
internal static class CssQuery
{
public static IEnumerable<SvgElement> QuerySelectorAll(this SvgElement elem, string selector)
{
var generator = new SelectorGenerator<SvgElement>(new SvgElementOps());
Fizzler.Parser.Parse(selector, generator);
return generator.Selector(Enumerable.Repeat(elem, 1));
}

public static int GetSpecificity(this BaseSelector selector)
{
if (selector is SimpleSelector)
{
var simpleCode = selector.ToString();
if (simpleCode.StartsWith("#"))
{
return 1 << 12;
}
else if (simpleCode.StartsWith("."))
{
return 1 << 8;
}
else
{
return 1 << 4;
}
}
return 0;
}
}
}
186 changes: 186 additions & 0 deletions Source/Css/SvgElementOps.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Fizzler;

namespace Svg.Css
{
internal class SvgElementOps : IElementOps<SvgElement>
{
public Selector<SvgElement> Type(NamespacePrefix prefix, string name)
{
var type = SvgElementFactory.AvailableElements.SingleOrDefault(e => e.ElementName == name);
return nodes => nodes.Where(n => n.GetType() == type.ElementType);
}

public Selector<SvgElement> Universal(NamespacePrefix prefix)
{
return nodes => nodes;
}

public Selector<SvgElement> Id(string id)
{
return nodes => nodes.Where(n => n.ID == id);
}

public Selector<SvgElement> Class(string clazz)
{
return AttributeIncludes(NamespacePrefix.None, "class", clazz);
}

public Selector<SvgElement> AttributeExists(NamespacePrefix prefix, string name)
{
return nodes => nodes.Where(n => n.Attributes.ContainsKey(name) || n.CustomAttributes.ContainsKey(name));
}

public Selector<SvgElement> AttributeExact(NamespacePrefix prefix, string name, string value)
{
return nodes => nodes.Where(n =>
{
string val = null;
object oval = null;
return (n.CustomAttributes.TryGetValue(name, out val) && val == value) ||
(n.Attributes.TryGetValue(name, out oval) && oval.ToString() == value);
});
}

public Selector<SvgElement> AttributeIncludes(NamespacePrefix prefix, string name, string value)
{
return nodes => nodes.Where(n =>
{
string val = null;
object oval = null;
return (n.CustomAttributes.TryGetValue(name, out val) && val.Split(' ').Contains(value)) ||
(n.Attributes.TryGetValue(name, out oval) && oval.ToString().Split(' ').Contains(value));
});
}

public Selector<SvgElement> AttributeDashMatch(NamespacePrefix prefix, string name, string value)
{
return string.IsNullOrEmpty(value)
? (Selector<SvgElement>)(nodes => Enumerable.Empty<SvgElement>())
: (nodes => nodes.Where(n =>
{
string val = null;
object oval = null;
return (n.CustomAttributes.TryGetValue(name, out val) && val.Split('-').Contains(value)) ||
(n.Attributes.TryGetValue(name, out oval) && oval.ToString().Split('-').Contains(value));
}));
}

public Selector<SvgElement> AttributePrefixMatch(NamespacePrefix prefix, string name, string value)
{
return string.IsNullOrEmpty(value)
? (Selector<SvgElement>)(nodes => Enumerable.Empty<SvgElement>())
: (nodes => nodes.Where(n =>
{
string val = null;
object oval = null;
return (n.CustomAttributes.TryGetValue(name, out val) && val.StartsWith(value)) ||
(n.Attributes.TryGetValue(name, out oval) && oval.ToString().StartsWith(value));
}));
}

public Selector<SvgElement> AttributeSuffixMatch(NamespacePrefix prefix, string name, string value)
{
return string.IsNullOrEmpty(value)
? (Selector<SvgElement>)(nodes => Enumerable.Empty<SvgElement>())
: (nodes => nodes.Where(n =>
{
string val = null;
object oval = null;
return (n.CustomAttributes.TryGetValue(name, out val) && val.EndsWith(value)) ||
(n.Attributes.TryGetValue(name, out oval) && oval.ToString().EndsWith(value));
}));
}

public Selector<SvgElement> AttributeSubstring(NamespacePrefix prefix, string name, string value)
{
return string.IsNullOrEmpty(value)
? (Selector<SvgElement>)(nodes => Enumerable.Empty<SvgElement>())
: (nodes => nodes.Where(n =>
{
string val = null;
object oval = null;
return (n.CustomAttributes.TryGetValue(name, out val) && val.Contains(value)) ||
(n.Attributes.TryGetValue(name, out oval) && oval.ToString().Contains(value));
}));
}

public Selector<SvgElement> FirstChild()
{
return nodes => nodes.Where(n => n.Parent == null || n.Parent.Children.First() == n);
}

public Selector<SvgElement> LastChild()
{
return nodes => nodes.Where(n => n.Parent == null || n.Parent.Children.Last() == n);
}

private IEnumerable<T> GetByIds<T>(IList<T> items, IEnumerable<int> indices)
{
foreach (var i in indices)
{
if (i >= 0 && i < items.Count) yield return items[i];
}
}

public Selector<SvgElement> NthChild(int a, int b)
{
return nodes => nodes.Where(n => n.Parent != null && GetByIds(n.Parent.Children, (from i in Enumerable.Range(0, n.Parent.Children.Count / a) select a * i + b)).Contains(n));
}

public Selector<SvgElement> OnlyChild()
{
return nodes => nodes.Where(n => n.Parent == null || n.Parent.Children.Count == 1);
}

public Selector<SvgElement> Empty()
{
return nodes => nodes.Where(n => n.Children.Count == 0);
}

public Selector<SvgElement> Child()
{
return nodes => nodes.SelectMany(n => n.Children);
}

public Selector<SvgElement> Descendant()
{
return nodes => nodes.SelectMany(n => Descendants(n));
}

private IEnumerable<SvgElement> Descendants(SvgElement elem)
{
foreach (var child in elem.Children)
{
yield return child;
foreach (var descendant in child.Descendants())
{
yield return descendant;
}
}
}

public Selector<SvgElement> Adjacent()
{
return nodes => nodes.SelectMany(n => ElementsAfterSelf(n).Take(1));
}

public Selector<SvgElement> GeneralSibling()
{
return nodes => nodes.SelectMany(n => ElementsAfterSelf(n));
}

private IEnumerable<SvgElement> ElementsAfterSelf(SvgElement self)
{
return (self.Parent == null ? Enumerable.Empty<SvgElement>() : self.Parent.Children.Skip(self.Parent.Children.IndexOf(self) + 1));
}

public Selector<SvgElement> NthLastChild(int a, int b)
{
throw new NotImplementedException();
}
}
}
7 changes: 7 additions & 0 deletions Source/External/ExCSS/IToString.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace ExCSS
{
public interface IToString
{
string ToString(bool friendlyFormat, int indentation = 0);
}
}
Loading

0 comments on commit 3aedd8e

Please sign in to comment.