Browse Source

Added style context

flabbet 7 months ago
parent
commit
28bad458b7

+ 2 - 1
src/PixiEditor.SVG/Features/IFillable.cs

@@ -1,4 +1,5 @@
-using PixiEditor.SVG.Units;
+using PixiEditor.SVG.Enums;
+using PixiEditor.SVG.Units;
 
 namespace PixiEditor.SVG.Features;
 

+ 84 - 0
src/PixiEditor.SVG/StyleContext.cs

@@ -0,0 +1,84 @@
+using PixiEditor.SVG.Features;
+using PixiEditor.SVG.Units;
+
+namespace PixiEditor.SVG;
+
+public struct StyleContext
+{
+    public SvgProperty<SvgNumericUnit> StrokeWidth { get; }
+    public SvgProperty<SvgColorUnit> Stroke { get; }
+    public SvgProperty<SvgColorUnit> Fill { get; }
+    public SvgProperty<SvgTransformUnit> Transform { get; }
+
+    public StyleContext()
+    {
+        StrokeWidth = new("stroke-width");
+        Stroke = new("stroke");
+        Fill = new("fill");
+        Transform = new("transform");
+    }
+    
+    public StyleContext(SvgDocument document)
+    {
+        StrokeWidth = document.StrokeWidth;
+        Stroke = document.Stroke;
+        Fill = document.Fill;
+        Transform = document.Transform;
+    }
+
+    public StyleContext WithElement(SvgElement element)
+    {
+        StyleContext styleContext = Copy();
+
+        if (element is ITransformable { Transform.Unit: not null } transformableElement)
+        {
+            styleContext.Transform.Unit = transformableElement.Transform.Unit;
+        }
+
+        if (element is IFillable { Fill.Unit: not null } fillableElement)
+        {
+            styleContext.Fill.Unit = fillableElement.Fill.Unit;
+        }
+
+        if (element is IStrokable strokableElement)
+        {
+            if (strokableElement.Stroke.Unit != null)
+            {
+                styleContext.Stroke.Unit = strokableElement.Stroke.Unit;
+            }
+
+            if (strokableElement.StrokeWidth.Unit != null)
+            {
+                styleContext.StrokeWidth.Unit = strokableElement.StrokeWidth.Unit;
+            }
+        }
+
+        return styleContext;
+    }
+
+    private StyleContext Copy()
+    {
+        StyleContext styleContext = new();
+        if (StrokeWidth.Unit != null)
+        {
+            styleContext.StrokeWidth.Unit = StrokeWidth.Unit;
+        }
+
+        if (Stroke.Unit != null)
+        {
+            styleContext.Stroke.Unit = Stroke.Unit;
+        }
+
+        if (Fill.Unit != null)
+        {
+            styleContext.Fill.Unit = Fill.Unit;
+        }
+
+        if (Transform.Unit != null)
+        {
+            styleContext.Transform.Unit = Transform.Unit;
+        }
+
+        return styleContext;
+    }
+}

+ 43 - 9
src/PixiEditor.SVG/SvgDocument.cs

@@ -1,4 +1,4 @@
-using System.Text;
+using System.Xml;
 using System.Xml.Linq;
 using Drawie.Numerics;
 using PixiEditor.SVG.Features;
@@ -6,17 +6,41 @@ using PixiEditor.SVG.Units;
 
 namespace PixiEditor.SVG;
 
-public class SvgDocument(RectD viewBox) : IElementContainer
+public class SvgDocument : SvgElement, IElementContainer, ITransformable, IFillable, IStrokable
 {
     public string RootNamespace { get; set; } = "http://www.w3.org/2000/svg";
     public string Version { get; set; } = "1.1";
-    public RectD ViewBox { get; set; } = viewBox;
     
+    public SvgProperty<SvgRectUnit> ViewBox { get; } = new("viewBox");
     public SvgProperty<SvgColorUnit> Fill { get; } = new("fill");
     public SvgProperty<SvgColorUnit> Stroke { get; } = new("stroke");
     public SvgProperty<SvgNumericUnit> StrokeWidth { get; } = new("stroke-width");
+    public SvgProperty<SvgTransformUnit> Transform { get; } = new("transform");
     public List<SvgElement> Children { get; } = new();
 
+    public SvgDocument() : base("svg")
+    {
+        
+    }
+    
+    public SvgDocument(RectD viewBox) : base("svg")
+    {
+        ViewBox.Unit = new SvgRectUnit(viewBox);
+    }
+
+    public override void ParseData(XmlReader reader)
+    {
+        List<SvgProperty> properties = new()
+        {
+            Fill,
+            Stroke,
+            StrokeWidth,
+            Transform
+        };
+        
+        ParseAttributes(properties, reader);
+    }
+
     public string ToXml()
     {
         XDocument document = new XDocument();
@@ -24,8 +48,7 @@ public class SvgDocument(RectD viewBox) : IElementContainer
         XNamespace ns = RootNamespace;
         document.Add(new XElement(
             ns + "svg",
-            new XAttribute("version", Version),
-            new XAttribute("viewBox", $"{ViewBox.X} {ViewBox.Y} {ViewBox.Width} {ViewBox.Height}"))
+            new XAttribute("version", Version))
         );
 
         Dictionary<string, string> usedNamespaces = new();
@@ -38,7 +61,7 @@ public class SvgDocument(RectD viewBox) : IElementContainer
         }
 
         AppendProperties(document.Root);
-        
+
         foreach (SvgElement child in Children)
         {
             document.Root.Add(child.ToXml(ns));
@@ -61,28 +84,39 @@ public class SvgDocument(RectD viewBox) : IElementContainer
             {
                 GatherRequiredNamespaces(usedNamespaces, container.Children);
             }
+
             foreach (KeyValuePair<string, string> ns in child.RequiredNamespaces)
             {
                 usedNamespaces[ns.Key] = ns.Value;
             }
         }
     }
-    
+
     private void AppendProperties(XElement? root)
     {
+        if(ViewBox.Unit != null)
+        {
+            root.Add(new XAttribute("viewBox", ViewBox.Unit.Value.ToXml()));
+        }
+        
         if (Fill.Unit != null)
         {
             root.Add(new XAttribute("fill", Fill.Unit.Value.ToXml()));
         }
-        
+
         if (Stroke.Unit != null)
         {
             root.Add(new XAttribute("stroke", Stroke.Unit.Value.ToXml()));
         }
-        
+
         if (StrokeWidth.Unit != null)
         {
             root.Add(new XAttribute("stroke-width", StrokeWidth.Unit.Value.ToXml()));
         }
+        
+        if (Transform.Unit != null)
+        {
+            root.Add(new XAttribute("transform", Transform.Unit.Value.ToXml()));
+        }
     }
 }

+ 9 - 4
src/PixiEditor.SVG/SvgParser.cs

@@ -3,6 +3,7 @@ using System.Xml.Linq;
 using Drawie.Numerics;
 using PixiEditor.SVG.Elements;
 using PixiEditor.SVG.Features;
+using PixiEditor.SVG.Units;
 
 namespace PixiEditor.SVG;
 
@@ -18,6 +19,7 @@ public class SvgParser
         { "g", typeof(SvgGroup) },
         { "mask", typeof(SvgMask) },
         { "image", typeof(SvgImage) },
+        { "svg", typeof(SvgDocument) }
     };
 
     public string Source { get; set; }
@@ -37,9 +39,12 @@ public class SvgParser
         {
             return null;
         }
+        
+        SvgDocument root = (SvgDocument)ParseElement(reader)!;
 
-        RectD bounds = ParseBounds(reader);
-        SvgDocument svgDocument = new(bounds);
+        RectD bounds = ParseBounds(reader); // this takes into account viewBox, width, height, x, y
+        
+        root.ViewBox.Unit = new SvgRectUnit(bounds);
 
         while (reader.Read())
         {
@@ -48,7 +53,7 @@ public class SvgParser
                 SvgElement? element = ParseElement(reader);
                 if (element != null)
                 {
-                    svgDocument.Children.Add(element);
+                    root.Children.Add(element);
 
                     if (element is IElementContainer container)
                     {
@@ -58,7 +63,7 @@ public class SvgParser
             }
         }
 
-        return svgDocument;
+        return root;
     }
 
     private void ParseChildren(XmlReader reader, IElementContainer container, string tagName)

+ 34 - 0
src/PixiEditor.SVG/Units/SvgRectUnit.cs

@@ -0,0 +1,34 @@
+using Drawie.Numerics;
+
+namespace PixiEditor.SVG.Units;
+
+public struct SvgRectUnit(RectD rect) : ISvgUnit
+{
+    public RectD Value { get; set; } = rect;
+    public string ToXml()
+    {
+        return $"{Value.X} {Value.Y} {Value.Width} {Value.Height}";
+    }
+
+    public void ValuesFromXml(string readerValue)
+    {
+        string[] values = readerValue.Split(' ');
+        
+        if (values.Length == 4)
+        {
+            double x, y, width, height;
+            
+            x = TryParseOrZero(values[0]);
+            y = TryParseOrZero(values[1]);
+            width = TryParseOrZero(values[2]);
+            height = TryParseOrZero(values[3]);
+            
+            Value = new RectD(x, y, width, height);
+        }
+        
+        double TryParseOrZero(string value)
+        {
+            return double.TryParse(value, out double result) ? result : 0;
+        }
+    }
+}

+ 6 - 0
src/PixiEditor.SVG/Utils/SvgColorUtility.cs

@@ -100,6 +100,12 @@ public static class SvgColorUtility
     {
         try
         {
+            if(input == "none")
+            {
+                color = Colors.Transparent;
+                return true;
+            }
+            
             SvgColorType colorType = ResolveColorType(input);
             float[]? values = ExtractColorValues(input, colorType);
             int requiredValues = colorType switch

+ 28 - 19
src/PixiEditor/Models/IO/CustomDocumentFormats/SvgDocumentBuilder.cs

@@ -23,19 +23,22 @@ internal class SvgDocumentBuilder : IDocumentBuilder
         string xml = File.ReadAllText(path);
         SvgDocument document = SvgDocument.Parse(xml);
 
-        builder.WithSize((int)document.ViewBox.Width, (int)document.ViewBox.Height)
+        StyleContext styleContext = new(document);
+
+        builder.WithSize((int)document.ViewBox.Unit.Value.Value.Width, (int)document.ViewBox.Unit.Value.Value.Height)
             .WithGraph(graph =>
             {
                 int? lastId = null;
                 foreach (SvgElement element in document.Children)
                 {
+                    StyleContext style = styleContext.WithElement(element);
                     if (element is SvgPrimitive primitive)
                     {
-                        lastId = AddPrimitive(element, primitive, graph, lastId);
+                        lastId = AddPrimitive(element, style, graph, lastId);
                     }
                     else if (element is SvgGroup group)
                     {
-                        lastId = AddGroup(group, graph, lastId);
+                        lastId = AddGroup(group, graph, style, lastId);
                     }
                 }
 
@@ -44,7 +47,8 @@ internal class SvgDocumentBuilder : IDocumentBuilder
     }
 
     [return: NotNull]
-    private int? AddPrimitive(SvgElement element, SvgPrimitive primitive, NodeGraphBuilder graph,
+    private int? AddPrimitive(SvgElement element, StyleContext styleContext,
+        NodeGraphBuilder graph,
         int? lastId, string connectionName = "Background")
     {
         LocalizedString name = "";
@@ -70,7 +74,7 @@ internal class SvgDocumentBuilder : IDocumentBuilder
             name = VectorRectangleToolViewModel.NewLayerKey;
         }
 
-        AddCommonShapeData(primitive, shapeData);
+        AddCommonShapeData(shapeData, styleContext);
 
         NodeGraphBuilder.NodeBuilder nBuilder = graph.WithNodeOfType<VectorLayerNode>(out int id)
             .WithName(name)
@@ -90,22 +94,22 @@ internal class SvgDocumentBuilder : IDocumentBuilder
         return lastId;
     }
 
-    private int? AddGroup(SvgGroup group, NodeGraphBuilder graph, int? lastId, string connectionName = "Background")
+    private int? AddGroup(SvgGroup group, NodeGraphBuilder graph, StyleContext style, int? lastId, string connectionName = "Background")
     {
-        string connectTo = FolderNode.ContentInternalName;
-
         int? childId = null;
-        connectTo = "Background";
+        var connectTo = "Background";
         
         foreach (var child in group.Children)
         {
+            StyleContext childStyle = style.WithElement(child);
+            
             if (child is SvgPrimitive primitive)
             {
-                childId = AddPrimitive(child, primitive, graph, childId, connectTo);
+                childId = AddPrimitive(child, childStyle, graph, childId, connectTo);
             }
             else if (child is SvgGroup childGroup)
             {
-                childId = AddGroup(childGroup, graph, childId, connectTo);
+                childId = AddGroup(childGroup, graph, childStyle, childId, connectTo);
             }
         }
 
@@ -180,32 +184,37 @@ internal class SvgDocumentBuilder : IDocumentBuilder
             element.Width.Unit.Value.Value, element.Height.Unit.Value.Value);
     }
 
-    private void AddCommonShapeData(SvgPrimitive primitive, ShapeVectorData? shapeData)
+    private void AddCommonShapeData(ShapeVectorData? shapeData, StyleContext styleContext)
     {
         if (shapeData == null)
         {
             return;
         }
 
-        bool hasFill = primitive.Fill.Unit is { Color.A: > 0 };
-        bool hasStroke = primitive.Stroke.Unit is { Color.A: > 0 };
-        bool hasTransform = primitive.Transform.Unit is { MatrixValue.IsIdentity: false };
+        bool hasFill = styleContext.Fill.Unit is { Color.A: > 0 };
+        bool hasStroke = styleContext.Stroke.Unit is { Color.A: > 0 };
+        bool hasTransform = styleContext.Transform.Unit is { MatrixValue.IsIdentity: false };
 
         shapeData.Fill = hasFill;
         if (hasFill)
         {
-            shapeData.FillColor = primitive.Fill.Unit.Value.Color;
+            var target = styleContext.Fill.Unit;
+            shapeData.FillColor = target.Value.Color;
         }
 
         if (hasStroke)
         {
-            shapeData.StrokeColor = primitive.Stroke.Unit.Value.Color;
-            shapeData.StrokeWidth = (float)primitive.StrokeWidth.Unit.Value.Value;
+            var targetColor = styleContext.Stroke.Unit;
+            var targetWidth = styleContext.StrokeWidth.Unit;
+            
+            shapeData.StrokeColor = targetColor.Value.Color;
+            shapeData.StrokeWidth = (float)targetWidth.Value.Value;
         }
 
         if (hasTransform)
         {
-            shapeData.TransformationMatrix = primitive.Transform.Unit.Value.MatrixValue;
+            var target = styleContext.Transform.Unit;
+            shapeData.TransformationMatrix = target.Value.MatrixValue;
         }
     }
 }