SvgElement.cs 37.9 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; }
        }

Eric Domke's avatar
Eric Domke committed
257
258
        private static readonly Matrix _zeroMatrix = new Matrix(0, 0, 0, 0, 0, 0);

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

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

Eric Domke's avatar
Eric Domke committed
275
            Matrix transformMatrix = renderer.Transform.Clone();
davescriven's avatar
davescriven committed
276
277
278

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

282
            renderer.Transform = transformMatrix;
283
284

            return true;
davescriven's avatar
davescriven committed
285
286
        }

287
        /// <summary>
Eric Domke's avatar
Eric Domke committed
288
        /// Removes any previously applied transforms from the specified <see cref="ISvgRenderer"/>.
289
        /// </summary>
Eric Domke's avatar
Eric Domke committed
290
291
        /// <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
292
        {
293
            renderer.Transform = _graphicsMatrix;
davescriven's avatar
davescriven committed
294
            _graphicsMatrix = null;
295
296
            renderer.SetClip(_graphicsClip);
            _graphicsClip = null;
davescriven's avatar
davescriven committed
297
298
        }

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

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

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

        /// <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
        {
342
            get { return this.Attributes.GetAttribute<string>("id"); }
davescriven's avatar
davescriven committed
343
344
            set
            {
tebjan's avatar
tebjan committed
345
                SetAndForceUniqueID(value, false);
346
347
            }
        }
davescriven's avatar
davescriven committed
348

Eric Domke's avatar
Eric Domke committed
349
350
351
352
353
354
355
356
357
358
359
        /// <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
360
        public void SetAndForceUniqueID(string value, bool autoForceUniqueID = true, Action<SvgElement, string, string> logElementOldIDNewID = null)
361
362
363
364
365
366
        {
            // Don't do anything if it hasn't changed
            if (string.Compare(this.ID, value) == 0)
            {
                return;
            }
davescriven's avatar
davescriven committed
367

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

373
            this.Attributes["id"] = value;
374
375
376

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

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

390
391
392
393
394
395
        /// <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>
396
        protected virtual void AddElement(SvgElement child, int index)
davescriven's avatar
davescriven committed
397
398
        {
        }
tebjan's avatar
tebjan committed
399
400
401
402
403
        
        /// <summary>
        /// Fired when an Element was added to the children of this Element
        /// </summary>
		public event EventHandler<ChildAddedEventArgs> ChildAdded;
davescriven's avatar
davescriven committed
404

405
406
407
408
409
        /// <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
410
411
        internal void OnElementAdded(SvgElement child, int index)
        {
412
            this.AddElement(child, index);
413
414
415
416
417
            SvgElement sibling = null;
            if(index < (Children.Count - 1))
            {
            	sibling = Children[index + 1];
            }
tebjan's avatar
tebjan committed
418
419
420
            var handler = ChildAdded;
            if(handler != null)
            {
421
            	handler(this, new ChildAddedEventArgs { NewChild = child, BeforeSibling = sibling });
tebjan's avatar
tebjan committed
422
            }
davescriven's avatar
davescriven committed
423
424
        }

425
426
427
428
429
        /// <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>
430
        protected virtual void RemoveElement(SvgElement child)
davescriven's avatar
davescriven committed
431
432
433
        {
        }

434
435
436
437
        /// <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
438
439
        internal void OnElementRemoved(SvgElement child)
        {
440
            this.RemoveElement(child);
davescriven's avatar
davescriven committed
441
442
443
444
445
        }

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

459
460
461
462
463
            //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
464

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

470
        }
471

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

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


484
        /// <summary>
Eric Domke's avatar
Eric Domke committed
485
        /// Renders this element to the <see cref="ISvgRenderer"/>.
486
        /// </summary>
Eric Domke's avatar
Eric Domke committed
487
488
        /// <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
489
        {
490
            this.Render(renderer);
davescriven's avatar
davescriven committed
491
492
493
494
        }

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

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

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

        protected virtual void WriteStartElement(XmlTextWriter writer)
        {
            if (this.ElementName != String.Empty)
            {
                writer.WriteStartElement(this.ElementName);
Tebjan Halm's avatar
Tebjan Halm committed
510
511
                if (this.ElementName == "svg")
                {
512
513
514
515
516
517
518
519
520
					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
521
522
523
524
525
526
527
528
529
530
531
532
533
534
            }
            this.WriteAttributes(writer);
        }

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

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

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

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

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

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

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

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

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

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

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

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

        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
636
637
638
639
640
            //write the content
            if(!String.IsNullOrEmpty(this.Content))
                writer.WriteString(this.Content);

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

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

658
659
660
        /// <summary>
        /// Renders the children of this <see cref="SvgElement"/>.
        /// </summary>
Eric Domke's avatar
Eric Domke committed
661
662
        /// <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
663
664
665
        {
            foreach (SvgElement element in this.Children)
            {
666
                element.Render(renderer);
davescriven's avatar
davescriven committed
667
668
669
            }
        }

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

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

                if (!(child is SvgPaintServer)) AddPaths(child, path);
Tebjan Halm's avatar
Tebjan Halm committed
706
        	}
Tebjan Halm's avatar
Tebjan Halm committed
707
708
709
710
711
712
713
        }
        
        /// <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
714
        protected GraphicsPath GetPaths(SvgElement elem, ISvgRenderer renderer)
Tebjan Halm's avatar
Tebjan Halm committed
715
716
717
718
719
720
721
722
723
        {
        	var ret = new GraphicsPath();
        	
        	foreach(var child in elem.Children)
        	{
        		if (child is SvgVisualElement)
        		{
        			if(!(child is SvgGroup))
        			{
724
                        var childPath = ((SvgVisualElement)child).Path(renderer);
Tebjan Halm's avatar
Tebjan Halm committed
725
726
727
728
729
730
731
732
733
734
735
736
        				
        				if (childPath != null)
        				{
        					childPath = (GraphicsPath)childPath.Clone();
        					if(child.Transforms != null)
        						childPath.Transform(child.Transforms.GetMatrix());
        					
        					ret.AddPath(childPath, false);
        				}
        			}
        			else
        			{
737
                        var childPath = GetPaths(child, renderer);
Tebjan Halm's avatar
Tebjan Halm committed
738
739
740
741
742
743
        				if(child.Transforms != null)
        					childPath.Transform(child.Transforms.GetMatrix());
        			}
        		}
        			
        	}
Tebjan Halm's avatar
Tebjan Halm committed
744
        	
Tebjan Halm's avatar
Tebjan Halm committed
745
        	return ret;
Tebjan Halm's avatar
Tebjan Halm committed
746
        }
davescriven's avatar
davescriven committed
747

748
749
750
751
752
753
        /// <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
754
755
756
757
        public virtual object Clone()
        {
            return this.MemberwiseClone();
        }
758
759
760
761
762
763

    	public abstract SvgElement DeepCopy();

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

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

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

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

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

785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
				//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
800
801
					else if (attr.Event.Name == "Click")
						newObj.Click += delegate {  };
802
803
					else if (attr.Event.Name == "Change") //text element
						(newObj as SvgText).Change += delegate {  };
804
805
				}
			}
Eric Domke's avatar
Eric Domke committed
806

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

815
			return newObj;
Tebjan Halm's avatar
Tebjan Halm committed
816
        }
Eric Domke's avatar
Eric Domke committed
817

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

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

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

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

        #region graphical EVENTS

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

Eric Domke's avatar
Eric Domke committed
860
#if Net4
861
862
863
        /// <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.
864
        /// The SvgTextElement overwrites this and regsiters the Change event tor its text content.
865
866
        /// </summary>
        /// <param name="caller"></param>
867
        public virtual void RegisterEvents(ISvgEventCaller caller)
868
        {
Tebjan Halm's avatar
Tebjan Halm committed
869
            if (caller != null && !string.IsNullOrEmpty(this.ID))
870
871
872
            {
                var rpcID = this.ID + "/";

873
874
875
876
877
878
                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));
879
                caller.RegisterAction<int, bool, bool, bool, string>(rpcID + "onmousescroll", OnMouseScroll);
880
881
            }
        }
882
883
884
885
886
        
        /// <summary>
        /// Use this method to provide your implementation ISvgEventCaller to unregister Actions
        /// </summary>
        /// <param name="caller"></param>
887
        public virtual void UnregisterEvents(ISvgEventCaller caller)
888
        {
Tebjan Halm's avatar
Tebjan Halm committed
889
        	if (caller != null && !string.IsNullOrEmpty(this.ID))
890
891
892
893
894
895
896
        	{
        		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
897
        		caller.UnregisterAction(rpcID + "onmousescroll");
898
899
900
901
        		caller.UnregisterAction(rpcID + "onmouseover");
        		caller.UnregisterAction(rpcID + "onmouseout");
        	}
        }
Eric Domke's avatar
Eric Domke committed
902
#endif
903

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

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

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

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

        [SvgAttribute("onmouseout")]
923
        public event EventHandler<MouseArg> MouseOut;
924
        
Eric Domke's avatar
Eric Domke committed
925
#if Net4
926
        protected Action<float, float, int, int, bool, bool, bool, string> CreateMouseEventAction(Action<object, MouseArg> eventRaiser)
Tebjan Halm's avatar
Tebjan Halm committed
927
        {
928
929
        	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 });
930
        }
Eric Domke's avatar
Eric Domke committed
931
932
#endif

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

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

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

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

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