SvgElement.cs 37.8 KB
Newer Older
davescriven's avatar
davescriven committed
1
2
3
4
5
6
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Xml;
7
using System.Linq;
davescriven's avatar
davescriven committed
8
using Svg.Transforms;
9
using System.Reflection;
Tebjan Halm's avatar
Tebjan Halm committed
10
11
using System.Threading;
using System.Globalization;
davescriven's avatar
davescriven committed
12
13
14
15
16
17

namespace Svg
{
    /// <summary>
    /// The base class of which all SVG elements are derived from.
    /// </summary>
18
    public abstract class SvgElement : ISvgElement, ISvgTransformable, ICloneable, ISvgNode
davescriven's avatar
davescriven committed
19
    {
20
        //optimization
21
        protected class PropertyAttributeTuple
22
        {
23
            public PropertyDescriptor Property;
24
25
            public SvgAttributeAttribute Attribute;
        }
26
27
28
29
30
31
32

        protected class EventAttributeTuple
        {
            public FieldInfo Event;
            public SvgAttributeAttribute Attribute;
        }

Tebjan Halm's avatar
Tebjan Halm committed
33
        //reflection cache
34
35
        private IEnumerable<PropertyAttributeTuple> _svgPropertyAttributes;
        private IEnumerable<EventAttributeTuple> _svgEventAttributes;
36

davescriven's avatar
davescriven committed
37
38
39
40
41
42
        internal SvgElement _parent;
        private string _elementName;
        private SvgAttributeCollection _attributes;
        private EventHandlerList _eventHandlers;
        private SvgElementCollection _children;
        private static readonly object _loadEventKey = new object();
43
        private Region _graphicsClip;
davescriven's avatar
davescriven committed
44
        private Matrix _graphicsMatrix;
45
        private SvgCustomAttributeCollection _customAttributes;
46
        private List<ISvgNode> _nodes = new List<ISvgNode>();
davescriven's avatar
davescriven committed
47

Eric Domke's avatar
Eric Domke committed
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

        private Dictionary<string, SortedDictionary<int, string>> _styles = new Dictionary<string, SortedDictionary<int, string>>();
        
        public void AddStyle(string name, string value, int specificity)
        {
            SortedDictionary<int, string> rules;
            if (!_styles.TryGetValue(name, out rules))
            {
                rules = new SortedDictionary<int, string>();
                _styles[name] = rules;
            }
            while (rules.ContainsKey(specificity)) specificity++;
            rules[specificity] = value;
        }
        public void FlushStyles()
        {
            foreach (var s in _styles)
            {
                SvgElementFactory.SetPropertyValue(this, s.Key, s.Value.Last().Value, this.OwnerDocument);
            }
            _styles = null;
        }

davescriven's avatar
davescriven committed
71
72
73
        /// <summary>
        /// Gets the name of the element.
        /// </summary>
74
        protected internal string ElementName
davescriven's avatar
davescriven committed
75
        {
76
77
78
79
80
81
82
83
84
85
86
87
88
89
            get
            {
                if (string.IsNullOrEmpty(this._elementName))
                {
                    var attr = TypeDescriptor.GetAttributes(this).OfType<SvgElementAttribute>().SingleOrDefault();

                    if (attr != null)
                    {
                        this._elementName = attr.ElementName;
                    }
                }

                return this._elementName;
            }
90
            internal set { this._elementName = value; }
davescriven's avatar
davescriven committed
91
92
        }

93
94
95
96
97
98
99
100
101
102
        /// <summary>
        /// Gets or sets the color <see cref="SvgPaintServer"/> of this element which drives the currentColor property.
        /// </summary>
        [SvgAttribute("color")]
        public virtual SvgPaintServer Color
        {
            get { return (this.Attributes["color"] == null) ? SvgColourServer.NotSet : (SvgPaintServer)this.Attributes["color"]; }
            set { this.Attributes["color"] = value; }
        }

davescriven's avatar
davescriven committed
103
104
105
        /// <summary>
        /// Gets or sets the content of the element.
        /// </summary>
Tebjan Halm's avatar
Tebjan Halm committed
106
        private string _content;
davescriven's avatar
davescriven committed
107
108
        public virtual string Content
        {
Tebjan Halm's avatar
Tebjan Halm committed
109
110
111
112
113
114
115
116
117
118
119
            get
            {
            	return _content;
            }
            set
            {
            	if(_content != null)
            	{
            		var oldVal = _content;
            		_content = value;
            		if(_content != oldVal)
tebjan's avatar
tebjan committed
120
            			OnContentChanged(new ContentEventArgs{ Content = value });
Tebjan Halm's avatar
Tebjan Halm committed
121
122
123
124
            	}
            	else
            	{
            		_content = value;
tebjan's avatar
tebjan committed
125
            		OnContentChanged(new ContentEventArgs{ Content = value });
Tebjan Halm's avatar
Tebjan Halm committed
126
127
            	}
            }
davescriven's avatar
davescriven committed
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
154
        }

        /// <summary>
        /// Gets an <see cref="EventHandlerList"/> of all events belonging to the element.
        /// </summary>
        protected virtual EventHandlerList Events
        {
            get { return this._eventHandlers; }
        }

        /// <summary>
        /// Occurs when the element is loaded.
        /// </summary>
        public event EventHandler Load
        {
            add { this.Events.AddHandler(_loadEventKey, value); }
            remove { this.Events.RemoveHandler(_loadEventKey, value); }
        }

        /// <summary>
        /// Gets a collection of all child <see cref="SvgElements"/>.
        /// </summary>
        public virtual SvgElementCollection Children
        {
            get { return this._children; }
        }

155
156
157
158
159
        public IList<ISvgNode> Nodes
        {
            get { return this._nodes; }
        }

Eric Domke's avatar
Eric Domke committed
160
161
162
163
164
165
166
167
168
        public IEnumerable<SvgElement> Descendants()
        {
            return this.AsEnumerable().Descendants();
        }
        private IEnumerable<SvgElement> AsEnumerable()
        {
            yield return this;
        }

davescriven's avatar
davescriven committed
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
        /// <summary>
        /// Gets a value to determine whether the element has children.
        /// </summary>
        public virtual bool HasChildren()
        {
            return (this.Children.Count > 0);
        }

        /// <summary>
        /// Gets the parent <see cref="SvgElement"/>.
        /// </summary>
        /// <value>An <see cref="SvgElement"/> if one exists; otherwise null.</value>
        public virtual SvgElement Parent
        {
            get { return this._parent; }
        }

186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
        public IEnumerable<SvgElement> Parents
        {
            get
            {
                var curr = this;
                while (curr.Parent != null)
                {
                    curr = curr.Parent;
                    yield return curr;
                }
            }
        }
        public IEnumerable<SvgElement> ParentsAndSelf
        {
            get
            {
                var curr = this;
                yield return curr;
                while (curr.Parent != null)
                {
                    curr = curr.Parent;
                    yield return curr;
                }
            }
        }

davescriven's avatar
davescriven committed
212
213
214
215
216
        /// <summary>
        /// Gets the owner <see cref="SvgDocument"/>.
        /// </summary>
        public virtual SvgDocument OwnerDocument
        {
217
218
219
220
221
222
223
224
225
226
227
228
229
230
        	get
        	{
        		if (this is SvgDocument)
        		{
        			return this as SvgDocument;
        		}
        		else
        		{
        			if(this.Parent != null)
        				return Parent.OwnerDocument;
        			else
        				return null;
        		}
        	}
davescriven's avatar
davescriven committed
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
        }

        /// <summary>
        /// Gets a collection of element attributes.
        /// </summary>
        protected internal virtual SvgAttributeCollection Attributes
        {
            get
            {
                if (this._attributes == null)
                {
                    this._attributes = new SvgAttributeCollection(this);
                }

                return this._attributes;
            }
        }

249
250
251
252
        /// <summary>
        /// Gets a collection of custom attributes
        /// </summary>
        public SvgCustomAttributeCollection CustomAttributes
253
254
255
256
        {
            get { return this._customAttributes; }
        }

257
        /// <summary>
Eric Domke's avatar
Eric Domke committed
258
        /// Applies the required transforms to <see cref="ISvgRenderer"/>.
259
        /// </summary>
Eric Domke's avatar
Eric Domke committed
260
261
        /// <param name="renderer">The <see cref="ISvgRenderer"/> to be transformed.</param>
        protected internal virtual bool PushTransforms(ISvgRenderer renderer)
davescriven's avatar
davescriven committed
262
        {
263
            _graphicsMatrix = renderer.Transform;
Eric Domke's avatar
Eric Domke committed
264
            _graphicsClip = renderer.GetClip();
265

davescriven's avatar
davescriven committed
266
267
268
            // Return if there are no transforms
            if (this.Transforms == null || this.Transforms.Count == 0)
            {
269
                return true;
davescriven's avatar
davescriven committed
270
            }
271
            if (this.Transforms.Count == 1 && this.Transforms[0].Matrix.Equals(new Matrix(0, 0, 0, 0, 0, 0))) return false;
davescriven's avatar
davescriven committed
272

273
            Matrix transformMatrix = renderer.Transform;
davescriven's avatar
davescriven committed
274
275
276

            foreach (SvgTransform transformation in this.Transforms)
            {
277
                transformMatrix.Multiply(transformation.Matrix);
davescriven's avatar
davescriven committed
278
279
            }

280
            renderer.Transform = transformMatrix;
281
282

            return true;
davescriven's avatar
davescriven committed
283
284
        }

285
        /// <summary>
Eric Domke's avatar
Eric Domke committed
286
        /// Removes any previously applied transforms from the specified <see cref="ISvgRenderer"/>.
287
        /// </summary>
Eric Domke's avatar
Eric Domke committed
288
289
        /// <param name="renderer">The <see cref="ISvgRenderer"/> that should have transforms removed.</param>
        protected internal virtual void PopTransforms(ISvgRenderer renderer)
davescriven's avatar
davescriven committed
290
        {
291
            renderer.Transform = _graphicsMatrix;
davescriven's avatar
davescriven committed
292
            _graphicsMatrix = null;
293
294
            renderer.SetClip(_graphicsClip);
            _graphicsClip = null;
davescriven's avatar
davescriven committed
295
296
        }

297
        /// <summary>
Eric Domke's avatar
Eric Domke committed
298
        /// Applies the required transforms to <see cref="ISvgRenderer"/>.
299
        /// </summary>
Eric Domke's avatar
Eric Domke committed
300
301
        /// <param name="renderer">The <see cref="ISvgRenderer"/> to be transformed.</param>
        void ISvgTransformable.PushTransforms(ISvgRenderer renderer)
davescriven's avatar
davescriven committed
302
        {
303
            this.PushTransforms(renderer);
davescriven's avatar
davescriven committed
304
305
        }

306
        /// <summary>
Eric Domke's avatar
Eric Domke committed
307
        /// Removes any previously applied transforms from the specified <see cref="ISvgRenderer"/>.
308
        /// </summary>
Eric Domke's avatar
Eric Domke committed
309
310
        /// <param name="renderer">The <see cref="ISvgRenderer"/> that should have transforms removed.</param>
        void ISvgTransformable.PopTransforms(ISvgRenderer renderer)
davescriven's avatar
davescriven committed
311
        {
312
            this.PopTransforms(renderer);
davescriven's avatar
davescriven committed
313
314
315
316
317
318
319
320
321
        }

        /// <summary>
        /// Gets or sets the element transforms.
        /// </summary>
        /// <value>The transforms.</value>
        [SvgAttribute("transform")]
        public SvgTransformCollection Transforms
        {
322
            get { return (this.Attributes.GetAttribute<SvgTransformCollection>("transform")); }
Tebjan Halm's avatar
Tebjan Halm committed
323
324
325
326
327
328
            set 
            { 
            	var old = this.Transforms;
            	if(old != null)
            		old.TransformChanged -= Attributes_AttributeChanged;
            	value.TransformChanged += Attributes_AttributeChanged;
329
            	this.Attributes["transform"] = value; 
Tebjan Halm's avatar
Tebjan Halm committed
330
            }
davescriven's avatar
davescriven committed
331
332
333
334
335
336
337
338
339
        }

        /// <summary>
        /// Gets or sets the ID of the element.
        /// </summary>
        /// <exception cref="SvgException">The ID is already used within the <see cref="SvgDocument"/>.</exception>
        [SvgAttribute("id")]
        public string ID
        {
340
            get { return this.Attributes.GetAttribute<string>("id"); }
davescriven's avatar
davescriven committed
341
342
            set
            {
tebjan's avatar
tebjan committed
343
                SetAndForceUniqueID(value, false);
344
345
            }
        }
davescriven's avatar
davescriven committed
346

Eric Domke's avatar
Eric Domke committed
347
348
349
350
351
352
353
354
355
356
357
        /// <summary>
        /// Gets or sets the text anchor.
        /// </summary>
        /// <value>The text anchor.</value>
        [SvgAttribute("space", SvgAttributeAttribute.XmlNamespace)]
        public virtual XmlSpaceHandling SpaceHandling
        {
            get { return (this.Attributes["space"] == null) ? XmlSpaceHandling.inherit : (XmlSpaceHandling)this.Attributes["space"]; }
            set { this.Attributes["space"] = value; }
        }

tebjan's avatar
tebjan committed
358
        public void SetAndForceUniqueID(string value, bool autoForceUniqueID = true, Action<SvgElement, string, string> logElementOldIDNewID = null)
359
360
361
362
363
364
        {
            // Don't do anything if it hasn't changed
            if (string.Compare(this.ID, value) == 0)
            {
                return;
            }
davescriven's avatar
davescriven committed
365

366
367
368
369
            if (this.OwnerDocument != null)
            {
                this.OwnerDocument.IdManager.Remove(this);
            }
davescriven's avatar
davescriven committed
370

371
            this.Attributes["id"] = value;
372
373
374

            if (this.OwnerDocument != null)
            {
tebjan's avatar
tebjan committed
375
                this.OwnerDocument.IdManager.AddAndForceUniqueID(this, null, autoForceUniqueID, logElementOldIDNewID);
davescriven's avatar
davescriven committed
376
377
378
            }
        }

379
380
381
382
        /// <summary>
        /// Only used by the ID Manager
        /// </summary>
        /// <param name="newID"></param>
tebjan's avatar
tebjan committed
383
        internal void ForceUniqueID(string newID)
384
        {
385
            this.Attributes["id"] = newID;
386
387
        }

388
389
390
391
392
393
        /// <summary>
        /// Called by the underlying <see cref="SvgElement"/> when an element has been added to the
        /// <see cref="Children"/> collection.
        /// </summary>
        /// <param name="child">The <see cref="SvgElement"/> that has been added.</param>
        /// <param name="index">An <see cref="int"/> representing the index where the element was added to the collection.</param>
394
        protected virtual void AddElement(SvgElement child, int index)
davescriven's avatar
davescriven committed
395
396
        {
        }
tebjan's avatar
tebjan committed
397
398
399
400
401
        
        /// <summary>
        /// Fired when an Element was added to the children of this Element
        /// </summary>
		public event EventHandler<ChildAddedEventArgs> ChildAdded;
davescriven's avatar
davescriven committed
402

403
404
405
406
407
        /// <summary>
        /// Calls the <see cref="AddElement"/> method with the specified parameters.
        /// </summary>
        /// <param name="child">The <see cref="SvgElement"/> that has been added.</param>
        /// <param name="index">An <see cref="int"/> representing the index where the element was added to the collection.</param>
davescriven's avatar
davescriven committed
408
409
        internal void OnElementAdded(SvgElement child, int index)
        {
410
            this.AddElement(child, index);
411
412
413
414
415
            SvgElement sibling = null;
            if(index < (Children.Count - 1))
            {
            	sibling = Children[index + 1];
            }
tebjan's avatar
tebjan committed
416
417
418
            var handler = ChildAdded;
            if(handler != null)
            {
419
            	handler(this, new ChildAddedEventArgs { NewChild = child, BeforeSibling = sibling });
tebjan's avatar
tebjan committed
420
            }
davescriven's avatar
davescriven committed
421
422
        }

423
424
425
426
427
        /// <summary>
        /// Called by the underlying <see cref="SvgElement"/> when an element has been removed from the
        /// <see cref="Children"/> collection.
        /// </summary>
        /// <param name="child">The <see cref="SvgElement"/> that has been removed.</param>
428
        protected virtual void RemoveElement(SvgElement child)
davescriven's avatar
davescriven committed
429
430
431
        {
        }

432
433
434
435
        /// <summary>
        /// Calls the <see cref="RemoveElement"/> method with the specified <see cref="SvgElement"/> as the parameter.
        /// </summary>
        /// <param name="child">The <see cref="SvgElement"/> that has been removed.</param>
davescriven's avatar
davescriven committed
436
437
        internal void OnElementRemoved(SvgElement child)
        {
438
            this.RemoveElement(child);
davescriven's avatar
davescriven committed
439
440
441
442
443
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SvgElement"/> class.
        /// </summary>
444
        public SvgElement()
davescriven's avatar
davescriven committed
445
446
447
448
        {
            this._children = new SvgElementCollection(this);
            this._eventHandlers = new EventHandlerList();
            this._elementName = string.Empty;
449
450
            this._customAttributes = new SvgCustomAttributeCollection(this);
            
Tebjan Halm's avatar
Tebjan Halm committed
451
452
            Transforms = new SvgTransformCollection();
            
453
454
455
            //subscribe to attribute events
            Attributes.AttributeChanged += Attributes_AttributeChanged;
            CustomAttributes.AttributeChanged += Attributes_AttributeChanged;
456

457
458
459
460
461
            //find svg attribute descriptions
            _svgPropertyAttributes = from PropertyDescriptor a in TypeDescriptor.GetProperties(this)
                            let attribute = a.Attributes[typeof(SvgAttributeAttribute)] as SvgAttributeAttribute
                            where attribute != null
                            select new PropertyAttributeTuple { Property = a, Attribute = attribute };
Tebjan Halm's avatar
Tebjan Halm committed
462

463
            _svgEventAttributes = from EventDescriptor a in TypeDescriptor.GetEvents(this)
464
465
                            let attribute = a.Attributes[typeof(SvgAttributeAttribute)] as SvgAttributeAttribute
                            where attribute != null
466
                            select new EventAttributeTuple { Event = a.ComponentType.GetField(a.Name, BindingFlags.Instance | BindingFlags.NonPublic), Attribute = attribute };
davescriven's avatar
davescriven committed
467

468
        }
469

470
471
472
473
474
475
        //dispatch attribute event
        void Attributes_AttributeChanged(object sender, AttributeEventArgs e)
        {
        	OnAttributeChanged(e);
        }

476
477
		public virtual void InitialiseFromXML(XmlTextReader reader, SvgDocument document)
		{
478
            throw new NotImplementedException();
479
480
481
		}


482
        /// <summary>
Eric Domke's avatar
Eric Domke committed
483
        /// Renders this element to the <see cref="ISvgRenderer"/>.
484
        /// </summary>
Eric Domke's avatar
Eric Domke committed
485
486
        /// <param name="renderer">The <see cref="ISvgRenderer"/> that the element should use to render itself.</param>
        public void RenderElement(ISvgRenderer renderer)
davescriven's avatar
davescriven committed
487
        {
488
            this.Render(renderer);
davescriven's avatar
davescriven committed
489
490
491
492
        }

        public void WriteElement(XmlTextWriter writer)
        {
Tebjan Halm's avatar
Tebjan Halm committed
493
494
495
496
            //Save previous culture and switch to invariant for writing
            var previousCulture = Thread.CurrentThread.CurrentCulture;
            Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;

davescriven's avatar
davescriven committed
497
            this.Write(writer);
Tebjan Halm's avatar
Tebjan Halm committed
498
499
500

            //Switch culture back
            Thread.CurrentThread.CurrentCulture = previousCulture;
davescriven's avatar
davescriven committed
501
502
503
504
505
506
507
        }

        protected virtual void WriteStartElement(XmlTextWriter writer)
        {
            if (this.ElementName != String.Empty)
            {
                writer.WriteStartElement(this.ElementName);
Tebjan Halm's avatar
Tebjan Halm committed
508
509
                if (this.ElementName == "svg")
                {
510
511
512
513
514
515
516
517
518
					foreach (var ns in SvgAttributeAttribute.Namespaces)
					{
						if (string.IsNullOrEmpty(ns.Key))
							writer.WriteAttributeString("xmlns", ns.Value);
						else
							writer.WriteAttributeString("xmlns:" + ns.Key, ns.Value);
					}
					writer.WriteAttributeString("version", "1.1");
				}
davescriven's avatar
davescriven committed
519
520
521
522
523
524
525
526
527
528
529
530
531
532
            }
            this.WriteAttributes(writer);
        }

        protected virtual void WriteEndElement(XmlTextWriter writer)
        {
            if (this.ElementName != String.Empty)
            {
                writer.WriteEndElement();
            }
        }

        protected virtual void WriteAttributes(XmlTextWriter writer)
        {
533
534
            //properties
            foreach (var attr in _svgPropertyAttributes)
535
536
537
538
539
            {
                if (attr.Property.Converter.CanConvertTo(typeof(string)))
                {
                    object propertyValue = attr.Property.GetValue(this);

540
541
542
                    var forceWrite = false;
                    if ((attr.Attribute.Name == "fill") && (Parent != null))
                    {
543
                    	if(propertyValue == SvgColourServer.NotSet) continue;
544
                    	
545
546
                        object parentValue;
                        if (TryResolveParentAttributeValue(attr.Attribute.Name, out parentValue))
547
                        {
548
549
                            if ((parentValue == propertyValue) 
                                || ((parentValue != null) &&  parentValue.Equals(propertyValue)))
550
                                continue;
551
                            
552
553
554
555
                            forceWrite = true;
                        }
                    }

556
557
                    if (propertyValue != null)
                    {
Tebjan Halm's avatar
Tebjan Halm committed
558
                        var type = propertyValue.GetType();
559
                        string value = (string)attr.Property.Converter.ConvertTo(propertyValue, typeof(string));
Tebjan Halm's avatar
Tebjan Halm committed
560

561
                        if (!SvgDefaults.IsDefault(attr.Attribute.Name, value) || forceWrite)
Tebjan Halm's avatar
Tebjan Halm committed
562
                        {
563
                            writer.WriteAttributeString(attr.Attribute.NamespaceAndName, value);
Tebjan Halm's avatar
Tebjan Halm committed
564
565
566
567
                        }
                    }
                    else if(attr.Attribute.Name == "fill") //if fill equals null, write 'none'
                    {
568
569
                        string value = (string)attr.Property.Converter.ConvertTo(propertyValue, typeof(string));
                        writer.WriteAttributeString(attr.Attribute.NamespaceAndName, value);
570
571
572
                    }
                }
            }
573

Tebjan Halm's avatar
Tebjan Halm committed
574
            
575
            //events
Tebjan Halm's avatar
Tebjan Halm committed
576
            if(AutoPublishEvents)
577
            {
578
579
580
	            foreach (var attr in _svgEventAttributes)
	            {
	                var evt = attr.Event.GetValue(this);
Eric Domke's avatar
Eric Domke committed
581

582
	                //if someone has registered publish the attribute
Tebjan Halm's avatar
Tebjan Halm committed
583
	                if (evt != null && !string.IsNullOrEmpty(this.ID))
584
585
586
587
	                {
	                    writer.WriteAttributeString(attr.Attribute.Name, this.ID + "/" + attr.Attribute.Name);
	                }
	            }
588
589
            }

590
591
592
593
594
            //add the custom attributes
            foreach (var item in this._customAttributes)
            {
                writer.WriteAttributeString(item.Key, item.Value);
            }
595
        }
Tebjan Halm's avatar
Tebjan Halm committed
596
597
        
        public bool AutoPublishEvents = true;
598

599
        private bool TryResolveParentAttributeValue(string attributeKey, out object parentAttributeValue)
600
        {
601
            parentAttributeValue = null;
602

tebjan's avatar
tebjan committed
603
            //attributeKey = char.ToUpper(attributeKey[0]) + attributeKey.Substring(1);
604
605

            var currentParent = Parent;
606
            var resolved = false;
607
608
609
610
            while (currentParent != null)
            {
                if (currentParent.Attributes.ContainsKey(attributeKey))
                {
611
612
613
                    resolved = true;
                    parentAttributeValue = currentParent.Attributes[attributeKey];
                    if (parentAttributeValue != null)
614
615
616
617
                        break;
                }
                currentParent = currentParent.Parent;
            }
618

619
            return resolved;
davescriven's avatar
davescriven committed
620
621
622
623
624
625
626
627
628
629
630
631
632
633
        }

        protected virtual void Write(XmlTextWriter writer)
        {
            if (this.ElementName != String.Empty)
            {
                this.WriteStartElement(writer);
                this.WriteChildren(writer);
                this.WriteEndElement(writer);
            }
        }

        protected virtual void WriteChildren(XmlTextWriter writer)
        {
Tebjan Halm's avatar
Tebjan Halm committed
634
635
636
637
638
            //write the content
            if(!String.IsNullOrEmpty(this.Content))
                writer.WriteString(this.Content);

            //write all children
davescriven's avatar
davescriven committed
639
640
641
642
643
644
645
            foreach (SvgElement child in this.Children)
            {
                child.Write(writer);
            }
        }

        /// <summary>
Eric Domke's avatar
Eric Domke committed
646
        /// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="ISvgRenderer"/> object.
davescriven's avatar
davescriven committed
647
        /// </summary>
Eric Domke's avatar
Eric Domke committed
648
649
        /// <param name="renderer">The <see cref="ISvgRenderer"/> object to render to.</param>
        protected virtual void Render(ISvgRenderer renderer)
davescriven's avatar
davescriven committed
650
        {
651
652
653
            this.PushTransforms(renderer);
            this.RenderChildren(renderer);
            this.PopTransforms(renderer);
davescriven's avatar
davescriven committed
654
655
        }

656
657
658
        /// <summary>
        /// Renders the children of this <see cref="SvgElement"/>.
        /// </summary>
Eric Domke's avatar
Eric Domke committed
659
660
        /// <param name="renderer">The <see cref="ISvgRenderer"/> to render the child <see cref="SvgElement"/>s to.</param>
        protected virtual void RenderChildren(ISvgRenderer renderer)
davescriven's avatar
davescriven committed
661
662
663
        {
            foreach (SvgElement element in this.Children)
            {
664
                element.Render(renderer);
davescriven's avatar
davescriven committed
665
666
667
            }
        }

668
        /// <summary>
Eric Domke's avatar
Eric Domke committed
669
        /// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="ISvgRenderer"/> object.
670
        /// </summary>
Eric Domke's avatar
Eric Domke committed
671
672
        /// <param name="renderer">The <see cref="ISvgRenderer"/> object to render to.</param>
        void ISvgElement.Render(ISvgRenderer renderer)
davescriven's avatar
davescriven committed
673
        {
674
            this.Render(renderer);
davescriven's avatar
davescriven committed
675
        }
Tebjan Halm's avatar
Tebjan Halm committed
676
677
678
679
680
681
682
683
        
        /// <summary>
        /// Recursive method to add up the paths of all children
        /// </summary>
        /// <param name="elem"></param>
        /// <param name="path"></param>
        protected void AddPaths(SvgElement elem, GraphicsPath path)
        {
Tebjan Halm's avatar
Tebjan Halm committed
684
        	foreach(var child in elem.Children)
Tebjan Halm's avatar
Tebjan Halm committed
685
        	{
Tebjan Halm's avatar
Tebjan Halm committed
686
        		if (child is SvgVisualElement)
Tebjan Halm's avatar
Tebjan Halm committed
687
        		{
Tebjan Halm's avatar
Tebjan Halm committed
688
        			if(!(child is SvgGroup))
689
        			{
690
        				var childPath = ((SvgVisualElement)child).Path(null);
691
        				
Tebjan Halm's avatar
Tebjan Halm committed
692
693
694
695
696
        				if (childPath != null)
        				{
        					childPath = (GraphicsPath)childPath.Clone();
        					if(child.Transforms != null)
        						childPath.Transform(child.Transforms.GetMatrix());
697
698

                            if (childPath.PointCount > 0) path.AddPath(childPath, false);
Tebjan Halm's avatar
Tebjan Halm committed
699
        				}
700
        			}
Tebjan Halm's avatar
Tebjan Halm committed
701
        		}
702
703

                if (!(child is SvgPaintServer)) AddPaths(child, path);
Tebjan Halm's avatar
Tebjan Halm committed
704
        	}
Tebjan Halm's avatar
Tebjan Halm committed
705
706
707
708
709
710
711
        }
        
        /// <summary>
        /// Recursive method to add up the paths of all children
        /// </summary>
        /// <param name="elem"></param>
        /// <param name="path"></param>
Eric Domke's avatar
Eric Domke committed
712
        protected GraphicsPath GetPaths(SvgElement elem, ISvgRenderer renderer)
Tebjan Halm's avatar
Tebjan Halm committed
713
714
715
716
717
718
719
720
721
        {
        	var ret = new GraphicsPath();
        	
        	foreach(var child in elem.Children)
        	{
        		if (child is SvgVisualElement)
        		{
        			if(!(child is SvgGroup))
        			{
722
                        var childPath = ((SvgVisualElement)child).Path(renderer);
Tebjan Halm's avatar
Tebjan Halm committed
723
724
725
726
727
728
729
730
731
732
733
734
        				
        				if (childPath != null)
        				{
        					childPath = (GraphicsPath)childPath.Clone();
        					if(child.Transforms != null)
        						childPath.Transform(child.Transforms.GetMatrix());
        					
        					ret.AddPath(childPath, false);
        				}
        			}
        			else
        			{
735
                        var childPath = GetPaths(child, renderer);
Tebjan Halm's avatar
Tebjan Halm committed
736
737
738
739
740
741
        				if(child.Transforms != null)
        					childPath.Transform(child.Transforms.GetMatrix());
        			}
        		}
        			
        	}
Tebjan Halm's avatar
Tebjan Halm committed
742
        	
Tebjan Halm's avatar
Tebjan Halm committed
743
        	return ret;
Tebjan Halm's avatar
Tebjan Halm committed
744
        }
davescriven's avatar
davescriven committed
745

746
747
748
749
750
751
        /// <summary>
        /// Creates a new object that is a copy of the current instance.
        /// </summary>
        /// <returns>
        /// A new object that is a copy of this instance.
        /// </returns>
davescriven's avatar
davescriven committed
752
753
754
755
        public virtual object Clone()
        {
            return this.MemberwiseClone();
        }
756
757
758
759
760
761

    	public abstract SvgElement DeepCopy();

		public virtual SvgElement DeepCopy<T>() where T : SvgElement, new()
		{
			var newObj = new T();
762
			newObj.ID = this.ID;
763
764
			newObj.Content = this.Content;
			newObj.ElementName = this.ElementName;
Eric Domke's avatar
Eric Domke committed
765

766
767
768
769
770
//			if (this.Parent != null)
	//			this.Parent.Children.Add(newObj);

			if (this.Transforms != null)
			{
771
				newObj.Transforms = this.Transforms.Clone() as SvgTransformCollection;
772
773
774
775
776
777
			}

			foreach (var child in this.Children)
			{
				newObj.Children.Add(child.DeepCopy());
			}
Eric Domke's avatar
Eric Domke committed
778

779
780
781
			foreach (var attr in this._svgEventAttributes)
			{
				var evt = attr.Event.GetValue(this);
Eric Domke's avatar
Eric Domke committed
782

783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
				//if someone has registered also register here
				if (evt != null)
				{
					if(attr.Event.Name == "MouseDown")
						newObj.MouseDown += delegate {  };
					else if (attr.Event.Name == "MouseUp")
						newObj.MouseUp += delegate {  };
					else if (attr.Event.Name == "MouseOver")
						newObj.MouseOver += delegate {  };
					else if (attr.Event.Name == "MouseOut")
						newObj.MouseOut += delegate {  };
					else if (attr.Event.Name == "MouseMove")
						newObj.MouseMove += delegate {  };
					else if (attr.Event.Name == "MouseScroll")
						newObj.MouseScroll += delegate {  };
Tebjan Halm's avatar
Tebjan Halm committed
798
799
					else if (attr.Event.Name == "Click")
						newObj.Click += delegate {  };
800
801
					else if (attr.Event.Name == "Change") //text element
						(newObj as SvgText).Change += delegate {  };
802
803
				}
			}
Eric Domke's avatar
Eric Domke committed
804

805
806
807
808
809
810
811
			if(this._customAttributes.Count > 0)
			{
				foreach (var element in _customAttributes) 
				{
					newObj.CustomAttributes.Add(element.Key, element.Value);
				}
			}
Eric Domke's avatar
Eric Domke committed
812

813
			return newObj;
Tebjan Halm's avatar
Tebjan Halm committed
814
        }
Eric Domke's avatar
Eric Domke committed
815

816
817
818
819
		/// <summary>
        /// Fired when an Atrribute of this Element has changed
        /// </summary>
		public event EventHandler<AttributeEventArgs> AttributeChanged;
Eric Domke's avatar
Eric Domke committed
820

821
822
823
824
825
826
827
828
		protected void OnAttributeChanged(AttributeEventArgs args)
		{
			var handler = AttributeChanged;
			if(handler != null)
			{
				handler(this, args);
			}
		}
Eric Domke's avatar
Eric Domke committed
829

tebjan's avatar
tebjan committed
830
831
832
833
		/// <summary>
        /// Fired when an Atrribute of this Element has changed
        /// </summary>
		public event EventHandler<ContentEventArgs> ContentChanged;
Eric Domke's avatar
Eric Domke committed
834

tebjan's avatar
tebjan committed
835
836
837
838
839
840
841
842
		protected void OnContentChanged(ContentEventArgs args)
		{
			var handler = ContentChanged;
			if(handler != null)
			{
				handler(this, args);
			}
		}
Tebjan Halm's avatar
Tebjan Halm committed
843
844
845
846

        #region graphical EVENTS

        /*  
847
848
849
            	onfocusin = "<anything>"
            	onfocusout = "<anything>"
            	onactivate = "<anything>"
850
851
852
853
854
                onclick = "<anything>"
                onmousedown = "<anything>"
                onmouseup = "<anything>"
                onmouseover = "<anything>"
                onmousemove = "<anything>"
855
            	onmouseout = "<anything>" 
Tebjan Halm's avatar
Tebjan Halm committed
856
857
         */

Eric Domke's avatar
Eric Domke committed
858
#if Net4
859
860
861
        /// <summary>
        /// Use this method to provide your implementation ISvgEventCaller which can register Actions 
        /// and call them if one of the events occurs. Make sure, that your SvgElement has a unique ID.
862
        /// The SvgTextElement overwrites this and regsiters the Change event tor its text content.
863
864
        /// </summary>
        /// <param name="caller"></param>
865
        public virtual void RegisterEvents(ISvgEventCaller caller)
866
        {
Tebjan Halm's avatar
Tebjan Halm committed
867
            if (caller != null && !string.IsNullOrEmpty(this.ID))
868
869
870
            {
                var rpcID = this.ID + "/";

871
872
873
874
875
876
                caller.RegisterAction<float, float, int, int, bool, bool, bool, string>(rpcID + "onclick", CreateMouseEventAction(RaiseMouseClick));
                caller.RegisterAction<float, float, int, int, bool, bool, bool, string>(rpcID + "onmousedown", CreateMouseEventAction(RaiseMouseDown));
                caller.RegisterAction<float, float, int, int, bool, bool, bool, string>(rpcID + "onmouseup", CreateMouseEventAction(RaiseMouseUp));
                caller.RegisterAction<float, float, int, int, bool, bool, bool, string>(rpcID + "onmousemove", CreateMouseEventAction(RaiseMouseMove));
                caller.RegisterAction<float, float, int, int, bool, bool, bool, string>(rpcID + "onmouseover", CreateMouseEventAction(RaiseMouseOver));
                caller.RegisterAction<float, float, int, int, bool, bool, bool, string>(rpcID + "onmouseout", CreateMouseEventAction(RaiseMouseOut));
877
                caller.RegisterAction<int, bool, bool, bool, string>(rpcID + "onmousescroll", OnMouseScroll);
878
879
            }
        }
880
881
882
883
884
        
        /// <summary>
        /// Use this method to provide your implementation ISvgEventCaller to unregister Actions
        /// </summary>
        /// <param name="caller"></param>
885
        public virtual void UnregisterEvents(ISvgEventCaller caller)
886
        {
Tebjan Halm's avatar
Tebjan Halm committed
887
        	if (caller != null && !string.IsNullOrEmpty(this.ID))
888
889
890
891
892
893
894
        	{
        		var rpcID = this.ID + "/";

        		caller.UnregisterAction(rpcID + "onclick");
        		caller.UnregisterAction(rpcID + "onmousedown");
        		caller.UnregisterAction(rpcID + "onmouseup");
        		caller.UnregisterAction(rpcID + "onmousemove");
joreg's avatar
joreg committed
895
        		caller.UnregisterAction(rpcID + "onmousescroll");
896
897
898
899
        		caller.UnregisterAction(rpcID + "onmouseover");
        		caller.UnregisterAction(rpcID + "onmouseout");
        	}
        }
Eric Domke's avatar
Eric Domke committed
900
#endif
901

Tebjan Halm's avatar
Tebjan Halm committed
902
903
904
        [SvgAttribute("onclick")]
        public event EventHandler<MouseArg> Click;

905
906
907
908
909
        [SvgAttribute("onmousedown")]
        public event EventHandler<MouseArg> MouseDown;

        [SvgAttribute("onmouseup")]
        public event EventHandler<MouseArg> MouseUp;
910
911
        
        [SvgAttribute("onmousemove")]
912
        public event EventHandler<MouseArg> MouseMove;
913

joreg's avatar
joreg committed
914
        [SvgAttribute("onmousescroll")]
915
        public event EventHandler<MouseScrollArg> MouseScroll;
joreg's avatar
joreg committed
916
        
917
        [SvgAttribute("onmouseover")]
918
        public event EventHandler<MouseArg> MouseOver;
919
920

        [SvgAttribute("onmouseout")]
921
        public event EventHandler<MouseArg> MouseOut;
922
        
Eric Domke's avatar
Eric Domke committed
923
#if Net4
924
        protected Action<float, float, int, int, bool, bool, bool, string> CreateMouseEventAction(Action<object, MouseArg> eventRaiser)
Tebjan Halm's avatar
Tebjan Halm committed
925
        {
926
927
        	return (x, y, button, clickCount, altKey, shiftKey, ctrlKey, sessionID) =>
        		eventRaiser(this, new MouseArg { x = x, y = y, Button = button, ClickCount = clickCount, AltKey = altKey, ShiftKey = shiftKey, CtrlKey = ctrlKey, SessionID = sessionID });
928
        }
Eric Domke's avatar
Eric Domke committed
929
930
#endif

931
        //click
932
933
934
935
936
937
        protected void RaiseMouseClick(object sender, MouseArg e)
        {
        	var handler = Click;
        	if (handler != null)
        	{
        		handler(sender, e);
938
939
940
            }
        }

941
        //down
Tebjan Halm's avatar
Tebjan Halm committed
942
943
944
        protected void RaiseMouseDown(object sender, MouseArg e)
        {
        	var handler = MouseDown;
945
946
            if (handler != null)
            {
Tebjan Halm's avatar
Tebjan Halm committed
947
                handler(sender, e);
948
949
950
            }
        }

951
952
953
954
        //up
        protected void RaiseMouseUp(object sender, MouseArg e)
        {
        	var handler = MouseUp;
955
956
            if (handler != null)
            {
957
                handler(sender, e);
958
959
            }
        }
960
961

        protected void RaiseMouseMove(object sender, MouseArg e)
962
963
        {
        	var handler = MouseMove;
964
965
            if (handler != null)
            {
966
                handler(sender, e);
967
968
            }
        }
joreg's avatar
joreg committed
969
        
970
971
        //over
        protected void RaiseMouseOver(object sender, MouseArg args)
972
973
        {
        	var handler = MouseOver;
974
975
            if (handler != null)
            {
Tebjan Halm's avatar
Tebjan Halm committed
976
                handler(sender, args);
Tebjan Halm's avatar
Tebjan Halm committed
977
978
979
            }
        }

980
        //out
981
        protected void RaiseMouseOut(object sender, MouseArg args)
982
        {
983
        	var handler = MouseOut;
984
985
            if (handler != null)
            {
Tebjan Halm's avatar
Tebjan Halm committed
986
                handler(sender, args);
987
988
            }
        }
joreg's avatar
joreg committed
989
        
990
991
        
        //scroll
992
        protected void OnMouseScroll(int scroll, bool ctrlKey, bool shiftKey, bool altKey, string sessionID)
joreg's avatar
joreg committed
993
        {
994
        	RaiseMouseScroll(this, new MouseScrollArg { Scroll = scroll, AltKey = altKey, ShiftKey = shiftKey, CtrlKey = ctrlKey, SessionID = sessionID});
joreg's avatar
joreg committed
995
996
        }
        
997
        protected void RaiseMouseScroll(object sender, MouseScrollArg e)
joreg's avatar
joreg committed
998
        {
999
        	var handler = MouseScroll;
joreg's avatar
joreg committed
1000
            if (handler != null)