SvgElementFactory.cs 8.17 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
            //Trace.TraceInformation("Begin CreateElement: {0}", elementName);
97

98
            if (elementName == "svg")
davescriven's avatar
davescriven committed
99
            {
100
101
102
103
                createdElement = (fragmentIsDocument) ? new SvgDocument() : new SvgFragment();
            }
            else
            {
104
105
                ElementInfo validType = AvailableElements.SingleOrDefault(e => e.ElementName == elementName);
                if (validType != null)
106
                {
107
                    createdElement = (SvgElement)Activator.CreateInstance(validType.ElementType);
108
                }
davescriven's avatar
davescriven committed
109
110
            }

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

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

            return createdElement;
        }

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

davescriven's avatar
davescriven committed
126
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
            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);
            }
153

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

        private static void SetPropertyValue(SvgElement element, string attributeName, string attributeValue, SvgDocument document)
        {
159
            var properties = TypeDescriptor.GetProperties(element.GetType(), new SvgAttributeAttribute[] { new SvgAttributeAttribute(attributeName) });
davescriven's avatar
davescriven committed
160
161
162

            if (properties.Count > 0)
            {
163
                PropertyDescriptor descriptor = properties[0];
davescriven's avatar
davescriven committed
164
165
166

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

176
177
178
        /// <summary>
        /// Contains information about a type inheriting from <see cref="SvgElement"/>.
        /// </summary>
179
180
        [DebuggerDisplay("{ElementName}, {ElementType}")]
        internal sealed class ElementInfo
181
        {
182
183
184
            /// <summary>
            /// Gets the SVG name of the <see cref="SvgElement"/>.
            /// </summary>
185
            public string ElementName { get; set; }
186
187
188
            /// <summary>
            /// Gets the <see cref="Type"/> of the <see cref="SvgElement"/> subclass.
            /// </summary>
189
190
            public Type ElementType { get; set; }

191
192
193
194
195
            /// <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>
196
197
198
199
200
            public ElementInfo(string elementName, Type elementType)
            {
                this.ElementName = elementName;
                this.ElementType = elementType;
            }
201
202
203
204
205
206
207

            /// <summary>
            /// Initializes a new instance of the <see cref="ElementInfo"/> class.
            /// </summary>
            public ElementInfo()
            {
            }
208
        }
davescriven's avatar
davescriven committed
209
210
    }
}