SvgElementFactory.cs 7.96 KB
Newer Older
davescriven's avatar
davescriven committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Collections;
using System.Text;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Xml;
using System.ComponentModel;
using System.Reflection;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Diagnostics;
using System.Threading;
15
using System.Linq;
davescriven's avatar
davescriven committed
16
17
18
19
20

using Svg.Transforms;

namespace Svg
{
21
22
23
    /// <summary>
    /// Provides the methods required in order to parse and create <see cref="SvgElement"/> instances from XML.
    /// </summary>
davescriven's avatar
davescriven committed
24
25
    internal class SvgElementFactory
    {
26
27
        private static List<ElementInfo> availableElements;

28
29
30
        /// <summary>
        /// Gets a list of available types that can be used when creating an <see cref="SvgElement"/>.
        /// </summary>
31
32
33
34
35
36
37
38
        private static List<ElementInfo> AvailableElements
        {
            get
            {
                if (availableElements == null)
                {
                    var svgTypes = from t in typeof(SvgDocument).Assembly.GetExportedTypes()
                                   where t.GetCustomAttributes(typeof(SvgElementAttribute), true).Length > 0
39
                                   && t.IsSubclassOf(typeof(SvgElement))
40
41
42
43
44
45
46
47
48
                                   select new ElementInfo { ElementName = ((SvgElementAttribute)t.GetCustomAttributes(typeof(SvgElementAttribute), true)[0]).ElementName, ElementType = t };

                    availableElements = svgTypes.ToList();
                }

                return availableElements;
            }
        }

49
50
51
52
53
54
        /// <summary>
        /// Creates an <see cref="SvgDocument"/> from the current node in the specified <see cref="XmlTextReader"/>.
        /// </summary>
        /// <param name="reader">The <see cref="XmlTextReader"/> containing the node to parse into an <see cref="SvgDocument"/>.</param>
        /// <exception cref="ArgumentNullException">The <paramref name="reader"/> parameter cannot be <c>null</c>.</exception>
        /// <exception cref="InvalidOperationException">The CreateDocument method can only be used to parse root &lt;svg&gt; elements.</exception>
davescriven's avatar
davescriven committed
55
56
        public static SvgDocument CreateDocument(XmlTextReader reader)
        {
57
58
59
60
61
62
63
64
65
66
            if (reader == null)
            {
                throw new ArgumentNullException("reader");
            }

            if (reader.LocalName != "svg")
            {
                throw new InvalidOperationException("The CreateDocument method can only be used to parse root <svg> elements.");
            }

davescriven's avatar
davescriven committed
67
68
69
            return (SvgDocument)CreateElement(reader, true, null);
        }

70
71
72
73
74
75
        /// <summary>
        /// Creates an <see cref="SvgElement"/> from the current node in the specified <see cref="XmlTextReader"/>.
        /// </summary>
        /// <param name="reader">The <see cref="XmlTextReader"/> containing the node to parse into a subclass of <see cref="SvgElement"/>.</param>
        /// <param name="document">The <see cref="SvgDocument"/> that the created element belongs to.</param>
        /// <exception cref="ArgumentNullException">The <paramref name="reader"/> and <paramref name="document"/> parameters cannot be <c>null</c>.</exception>
davescriven's avatar
davescriven committed
76
77
        public static SvgElement CreateElement(XmlTextReader reader, SvgDocument document)
        {
78
79
80
81
82
83
84
85
86
87
            if (reader == null)
            {
                throw new ArgumentNullException("reader");
            }

            if (document == null)
            {
                throw new ArgumentNullException("document");
            }

davescriven's avatar
davescriven committed
88
89
90
91
92
93
94
95
            return CreateElement(reader, false, document);
        }

        private static SvgElement CreateElement(XmlTextReader reader, bool fragmentIsDocument, SvgDocument document)
        {
            SvgElement createdElement = null;
            string elementName = reader.LocalName;

96
97
            Trace.TraceInformation("Begin CreateElement: {0}", elementName);

98
            if (elementName == "svg")
davescriven's avatar
davescriven committed
99
            {
100
101
102
103
104
                createdElement = (fragmentIsDocument) ? new SvgDocument() : new SvgFragment();
            }
            else
            {
                var validTypes = AvailableElements.Where(e => e.ElementName == elementName);
davescriven's avatar
davescriven committed
105

106
107
108
109
                if (validTypes.Count() > 0)
                {
                    createdElement = Activator.CreateInstance(validTypes.First().ElementType) as SvgElement;
                }
davescriven's avatar
davescriven committed
110
111
            }

112
113
114
115
116
117
118
            if (createdElement != null)
            {
                createdElement.ElementName = elementName;
                SetAttributes(createdElement, reader, document);
            }

            Trace.TraceInformation("End CreateElement");
davescriven's avatar
davescriven committed
119
120
121
122
123
124

            return createdElement;
        }

        private static void SetAttributes(SvgElement element, XmlTextReader reader, SvgDocument document)
        {
125
126
            Trace.TraceInformation("Begin SetAttributes");

davescriven's avatar
davescriven committed
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
            string[] styles = null;
            string[] style = null;
            int i = 0;

            while (reader.MoveToNextAttribute())
            {
                // Special treatment for "style"
                if (reader.LocalName.Equals("style"))
                {
                    styles = reader.Value.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);

                    for (i = 0; i < styles.Length; i++)
                    {
                        if (!styles[i].Contains(":"))
                        {
                            continue;
                        }

                        style = styles[i].Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
                        SetPropertyValue(element, style[0].Trim(), style[1].Trim(), document);
                    }

                    continue;
                }

                SetPropertyValue(element, reader.LocalName, reader.Value, document);
            }
154
155

            Trace.TraceInformation("End SetAttributes");
davescriven's avatar
davescriven committed
156
157
158
159
        }

        private static void SetPropertyValue(SvgElement element, string attributeName, string attributeValue, SvgDocument document)
        {
160
            var properties = TypeDescriptor.GetProperties(element.GetType(), new SvgAttributeAttribute[] { new SvgAttributeAttribute(attributeName) });
davescriven's avatar
davescriven committed
161
162
163
164
165
166
167
168
            PropertyDescriptor descriptor = null;

            if (properties.Count > 0)
            {
                descriptor = properties[0];

                try
                {
169
                    descriptor.SetValue(element, descriptor.Converter.ConvertFrom(document, CultureInfo.InvariantCulture, attributeValue));
davescriven's avatar
davescriven committed
170
171
172
173
174
175
176
                }
                catch
                {
                    Trace.TraceWarning(string.Format("Attribute '{0}' cannot be set - type '{1}' cannot convert from string '{2}'.", attributeName, descriptor.PropertyType.FullName, attributeValue));
                }
            }
        }
177

178
179
180
        /// <summary>
        /// Contains information about a type inheriting from <see cref="SvgElement"/>.
        /// </summary>
181
182
        private struct ElementInfo
        {
183
184
185
            /// <summary>
            /// Gets the SVG name of the <see cref="SvgElement"/>.
            /// </summary>
186
            public string ElementName { get; set; }
187
188
189
            /// <summary>
            /// Gets the <see cref="Type"/> of the <see cref="SvgElement"/> subclass.
            /// </summary>
190
191
            public Type ElementType { get; set; }

192
193
194
195
196
            /// <summary>
            /// Initializes a new instance of the <see cref="ElementInfo"/> struct.
            /// </summary>
            /// <param name="elementName">Name of the element.</param>
            /// <param name="elementType">Type of the element.</param>
197
198
199
200
201
202
203
            public ElementInfo(string elementName, Type elementType)
                : this()
            {
                this.ElementName = elementName;
                this.ElementType = elementType;
            }
        }
davescriven's avatar
davescriven committed
204
205
    }
}