SvgElement.cs 39.2 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>
Eric Domke's avatar
Eric Domke committed
18
    public abstract partial class SvgElement : ISvgElement, ISvgTransformable, ICloneable, ISvgNode
davescriven's avatar
davescriven committed
19
    {
Eric Domke's avatar
Eric Domke committed
20
21
22
        internal const int StyleSpecificity_PresAttribute = 0;
        internal const int StyleSpecificity_InlineStyle = 1 << 16;

23
        //optimization
24
        protected class PropertyAttributeTuple
25
        {
26
            public PropertyDescriptor Property;
27
28
            public SvgAttributeAttribute Attribute;
        }
29
30
31
32
33
34
35

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

Tebjan Halm's avatar
Tebjan Halm committed
36
        //reflection cache
37
38
        private IEnumerable<PropertyAttributeTuple> _svgPropertyAttributes;
        private IEnumerable<EventAttributeTuple> _svgEventAttributes;
39

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

Eric Domke's avatar
Eric Domke committed
51
52
53

        private Dictionary<string, SortedDictionary<int, string>> _styles = new Dictionary<string, SortedDictionary<int, string>>();
        
Eric Domke's avatar
Eric Domke committed
54

Eric Domke's avatar
Eric Domke committed
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
        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;
        }

Eric Domke's avatar
Eric Domke committed
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100

        public bool ContainsAttribute(string name)
        {
            SortedDictionary<int, string> rules;
            return (this.Attributes.ContainsKey(name) || this.CustomAttributes.ContainsKey(name) ||
                (_styles.TryGetValue(name, out rules) && (rules.ContainsKey(StyleSpecificity_InlineStyle) || rules.ContainsKey(StyleSpecificity_PresAttribute))));
        }
        public bool TryGetAttribute(string name, out string value)
        {
            object objValue;
            if (this.Attributes.TryGetValue(name, out objValue))
            {
                value = objValue.ToString();
                return true;
            }
            if (this.CustomAttributes.TryGetValue(name, out value)) return true;
            SortedDictionary<int, string> rules;
            if (_styles.TryGetValue(name, out rules))
            {
                // Get staged styles that are 
                if (rules.TryGetValue(StyleSpecificity_InlineStyle, out value)) return true;
                if (rules.TryGetValue(StyleSpecificity_PresAttribute, out value)) return true;
            }
            return false;
        }

davescriven's avatar
davescriven committed
101
102
103
        /// <summary>
        /// Gets the name of the element.
        /// </summary>
104
        protected internal string ElementName
davescriven's avatar
davescriven committed
105
        {
106
107
108
109
110
111
112
113
114
115
116
117
118
119
            get
            {
                if (string.IsNullOrEmpty(this._elementName))
                {
                    var attr = TypeDescriptor.GetAttributes(this).OfType<SvgElementAttribute>().SingleOrDefault();

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

                return this._elementName;
            }
120
            internal set { this._elementName = value; }
davescriven's avatar
davescriven committed
121
122
        }

123
124
125
126
127
128
129
130
131
132
        /// <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
133
134
135
        /// <summary>
        /// Gets or sets the content of the element.
        /// </summary>
Tebjan Halm's avatar
Tebjan Halm committed
136
        private string _content;
davescriven's avatar
davescriven committed
137
138
        public virtual string Content
        {
Tebjan Halm's avatar
Tebjan Halm committed
139
140
141
142
143
144
145
146
147
148
149
            get
            {
            	return _content;
            }
            set
            {
            	if(_content != null)
            	{
            		var oldVal = _content;
            		_content = value;
            		if(_content != oldVal)
tebjan's avatar
tebjan committed
150
            			OnContentChanged(new ContentEventArgs{ Content = value });
Tebjan Halm's avatar
Tebjan Halm committed
151
152
153
154
            	}
            	else
            	{
            		_content = value;
tebjan's avatar
tebjan committed
155
            		OnContentChanged(new ContentEventArgs{ Content = value });
Tebjan Halm's avatar
Tebjan Halm committed
156
157
            	}
            }
davescriven's avatar
davescriven committed
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
        }

        /// <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; }
        }

185
186
187
188
189
        public IList<ISvgNode> Nodes
        {
            get { return this._nodes; }
        }

Eric Domke's avatar
Eric Domke committed
190
191
192
193
194
195
196
197
198
        public IEnumerable<SvgElement> Descendants()
        {
            return this.AsEnumerable().Descendants();
        }
        private IEnumerable<SvgElement> AsEnumerable()
        {
            yield return this;
        }

davescriven's avatar
davescriven committed
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
        /// <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; }
        }

216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
        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
242
243
244
245
246
        /// <summary>
        /// Gets the owner <see cref="SvgDocument"/>.
        /// </summary>
        public virtual SvgDocument OwnerDocument
        {
247
248
249
250
251
252
253
254
255
256
257
258
259
260
        	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
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
        }

        /// <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;
            }
        }

279
280
281
282
        /// <summary>
        /// Gets a collection of custom attributes
        /// </summary>
        public SvgCustomAttributeCollection CustomAttributes
283
284
285
286
        {
            get { return this._customAttributes; }
        }

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

289
        /// <summary>
Eric Domke's avatar
Eric Domke committed
290
        /// Applies the required transforms to <see cref="ISvgRenderer"/>.
291
        /// </summary>
Eric Domke's avatar
Eric Domke committed
292
293
        /// <param name="renderer">The <see cref="ISvgRenderer"/> to be transformed.</param>
        protected internal virtual bool PushTransforms(ISvgRenderer renderer)
davescriven's avatar
davescriven committed
294
        {
295
            _graphicsMatrix = renderer.Transform;
Eric Domke's avatar
Eric Domke committed
296
            _graphicsClip = renderer.GetClip();
297

davescriven's avatar
davescriven committed
298
299
300
            // Return if there are no transforms
            if (this.Transforms == null || this.Transforms.Count == 0)
            {
301
                return true;
davescriven's avatar
davescriven committed
302
            }
Eric Domke's avatar
Eric Domke committed
303
            if (this.Transforms.Count == 1 && this.Transforms[0].Matrix.Equals(_zeroMatrix)) return false;
davescriven's avatar
davescriven committed
304

Eric Domke's avatar
Eric Domke committed
305
            Matrix transformMatrix = renderer.Transform.Clone();
davescriven's avatar
davescriven committed
306
307
308

            foreach (SvgTransform transformation in this.Transforms)
            {
309
                transformMatrix.Multiply(transformation.Matrix);
davescriven's avatar
davescriven committed
310
311
            }

312
            renderer.Transform = transformMatrix;
313
314

            return true;
davescriven's avatar
davescriven committed
315
316
        }

317
        /// <summary>
Eric Domke's avatar
Eric Domke committed
318
        /// Removes any previously applied transforms from the specified <see cref="ISvgRenderer"/>.
319
        /// </summary>
Eric Domke's avatar
Eric Domke committed
320
321
        /// <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
322
        {
323
            renderer.Transform = _graphicsMatrix;
davescriven's avatar
davescriven committed
324
            _graphicsMatrix = null;
325
326
            renderer.SetClip(_graphicsClip);
            _graphicsClip = null;
davescriven's avatar
davescriven committed
327
328
        }

329
        /// <summary>
Eric Domke's avatar
Eric Domke committed
330
        /// Applies the required transforms to <see cref="ISvgRenderer"/>.
331
        /// </summary>
Eric Domke's avatar
Eric Domke committed
332
333
        /// <param name="renderer">The <see cref="ISvgRenderer"/> to be transformed.</param>
        void ISvgTransformable.PushTransforms(ISvgRenderer renderer)
davescriven's avatar
davescriven committed
334
        {
335
            this.PushTransforms(renderer);
davescriven's avatar
davescriven committed
336
337
        }

338
        /// <summary>
Eric Domke's avatar
Eric Domke committed
339
        /// Removes any previously applied transforms from the specified <see cref="ISvgRenderer"/>.
340
        /// </summary>
Eric Domke's avatar
Eric Domke committed
341
342
        /// <param name="renderer">The <see cref="ISvgRenderer"/> that should have transforms removed.</param>
        void ISvgTransformable.PopTransforms(ISvgRenderer renderer)
davescriven's avatar
davescriven committed
343
        {
344
            this.PopTransforms(renderer);
davescriven's avatar
davescriven committed
345
346
347
348
349
350
351
352
353
        }

        /// <summary>
        /// Gets or sets the element transforms.
        /// </summary>
        /// <value>The transforms.</value>
        [SvgAttribute("transform")]
        public SvgTransformCollection Transforms
        {
354
            get { return (this.Attributes.GetAttribute<SvgTransformCollection>("transform")); }
Tebjan Halm's avatar
Tebjan Halm committed
355
356
357
358
359
360
            set 
            { 
            	var old = this.Transforms;
            	if(old != null)
            		old.TransformChanged -= Attributes_AttributeChanged;
            	value.TransformChanged += Attributes_AttributeChanged;
361
            	this.Attributes["transform"] = value; 
Tebjan Halm's avatar
Tebjan Halm committed
362
            }
davescriven's avatar
davescriven committed
363
364
365
366
367
368
369
370
371
        }

        /// <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
        {
372
            get { return this.Attributes.GetAttribute<string>("id"); }
davescriven's avatar
davescriven committed
373
374
            set
            {
tebjan's avatar
tebjan committed
375
                SetAndForceUniqueID(value, false);
376
377
            }
        }
davescriven's avatar
davescriven committed
378

Eric Domke's avatar
Eric Domke committed
379
380
381
382
383
384
385
386
387
388
389
        /// <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
390
        public void SetAndForceUniqueID(string value, bool autoForceUniqueID = true, Action<SvgElement, string, string> logElementOldIDNewID = null)
391
392
393
394
395
396
        {
            // Don't do anything if it hasn't changed
            if (string.Compare(this.ID, value) == 0)
            {
                return;
            }
davescriven's avatar
davescriven committed
397

398
399
400
401
            if (this.OwnerDocument != null)
            {
                this.OwnerDocument.IdManager.Remove(this);
            }
davescriven's avatar
davescriven committed
402

403
            this.Attributes["id"] = value;
404
405
406

            if (this.OwnerDocument != null)
            {
tebjan's avatar
tebjan committed
407
                this.OwnerDocument.IdManager.AddAndForceUniqueID(this, null, autoForceUniqueID, logElementOldIDNewID);
davescriven's avatar
davescriven committed
408
409
410
            }
        }

411
412
413
414
        /// <summary>
        /// Only used by the ID Manager
        /// </summary>
        /// <param name="newID"></param>
tebjan's avatar
tebjan committed
415
        internal void ForceUniqueID(string newID)
416
        {
417
            this.Attributes["id"] = newID;
418
419
        }

420
421
422
423
424
425
        /// <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>
426
        protected virtual void AddElement(SvgElement child, int index)
davescriven's avatar
davescriven committed
427
428
        {
        }
tebjan's avatar
tebjan committed
429
430
431
432
433
        
        /// <summary>
        /// Fired when an Element was added to the children of this Element
        /// </summary>
		public event EventHandler<ChildAddedEventArgs> ChildAdded;
davescriven's avatar
davescriven committed
434

435
436
437
438
439
        /// <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
440
441
        internal void OnElementAdded(SvgElement child, int index)
        {
442
            this.AddElement(child, index);
443
444
445
446
447
            SvgElement sibling = null;
            if(index < (Children.Count - 1))
            {
            	sibling = Children[index + 1];
            }
tebjan's avatar
tebjan committed
448
449
450
            var handler = ChildAdded;
            if(handler != null)
            {
451
            	handler(this, new ChildAddedEventArgs { NewChild = child, BeforeSibling = sibling });
tebjan's avatar
tebjan committed
452
            }
davescriven's avatar
davescriven committed
453
454
        }

455
456
457
458
459
        /// <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>
460
        protected virtual void RemoveElement(SvgElement child)
davescriven's avatar
davescriven committed
461
462
463
        {
        }

464
465
466
467
        /// <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
468
469
        internal void OnElementRemoved(SvgElement child)
        {
470
            this.RemoveElement(child);
davescriven's avatar
davescriven committed
471
472
473
474
475
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SvgElement"/> class.
        /// </summary>
476
        public SvgElement()
davescriven's avatar
davescriven committed
477
478
479
480
        {
            this._children = new SvgElementCollection(this);
            this._eventHandlers = new EventHandlerList();
            this._elementName = string.Empty;
481
482
            this._customAttributes = new SvgCustomAttributeCollection(this);
            
Tebjan Halm's avatar
Tebjan Halm committed
483
484
            Transforms = new SvgTransformCollection();
            
485
486
487
            //subscribe to attribute events
            Attributes.AttributeChanged += Attributes_AttributeChanged;
            CustomAttributes.AttributeChanged += Attributes_AttributeChanged;
488

489
490
491
492
493
            //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
494

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

500
        }
501

502
503
504
505
506
507
        //dispatch attribute event
        void Attributes_AttributeChanged(object sender, AttributeEventArgs e)
        {
        	OnAttributeChanged(e);
        }

508
509
		public virtual void InitialiseFromXML(XmlTextReader reader, SvgDocument document)
		{
510
            throw new NotImplementedException();
511
512
513
		}


514
        /// <summary>
Eric Domke's avatar
Eric Domke committed
515
        /// Renders this element to the <see cref="ISvgRenderer"/>.
516
        /// </summary>
Eric Domke's avatar
Eric Domke committed
517
518
        /// <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
519
        {
520
            this.Render(renderer);
davescriven's avatar
davescriven committed
521
522
523
524
        }

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

davescriven's avatar
davescriven committed
529
            this.Write(writer);
Tebjan Halm's avatar
Tebjan Halm committed
530
531
532

            //Switch culture back
            Thread.CurrentThread.CurrentCulture = previousCulture;
davescriven's avatar
davescriven committed
533
534
535
536
537
538
539
        }

        protected virtual void WriteStartElement(XmlTextWriter writer)
        {
            if (this.ElementName != String.Empty)
            {
                writer.WriteStartElement(this.ElementName);
Tebjan Halm's avatar
Tebjan Halm committed
540
541
                if (this.ElementName == "svg")
                {
542
543
544
545
546
547
548
549
550
					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
551
552
553
554
555
556
557
558
559
560
561
562
563
564
            }
            this.WriteAttributes(writer);
        }

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

        protected virtual void WriteAttributes(XmlTextWriter writer)
        {
565
566
            //properties
            foreach (var attr in _svgPropertyAttributes)
567
568
569
570
571
            {
                if (attr.Property.Converter.CanConvertTo(typeof(string)))
                {
                    object propertyValue = attr.Property.GetValue(this);

572
573
574
                    var forceWrite = false;
                    if ((attr.Attribute.Name == "fill") && (Parent != null))
                    {
575
                    	if(propertyValue == SvgColourServer.NotSet) continue;
576
                    	
577
578
                        object parentValue;
                        if (TryResolveParentAttributeValue(attr.Attribute.Name, out parentValue))
579
                        {
580
581
                            if ((parentValue == propertyValue) 
                                || ((parentValue != null) &&  parentValue.Equals(propertyValue)))
582
                                continue;
583
                            
584
585
586
587
                            forceWrite = true;
                        }
                    }

588
589
                    if (propertyValue != null)
                    {
Tebjan Halm's avatar
Tebjan Halm committed
590
                        var type = propertyValue.GetType();
591
                        string value = (string)attr.Property.Converter.ConvertTo(propertyValue, typeof(string));
Tebjan Halm's avatar
Tebjan Halm committed
592

593
                        if (!SvgDefaults.IsDefault(attr.Attribute.Name, value) || forceWrite)
Tebjan Halm's avatar
Tebjan Halm committed
594
                        {
595
                            writer.WriteAttributeString(attr.Attribute.NamespaceAndName, value);
Tebjan Halm's avatar
Tebjan Halm committed
596
597
598
599
                        }
                    }
                    else if(attr.Attribute.Name == "fill") //if fill equals null, write 'none'
                    {
600
601
                        string value = (string)attr.Property.Converter.ConvertTo(propertyValue, typeof(string));
                        writer.WriteAttributeString(attr.Attribute.NamespaceAndName, value);
602
603
604
                    }
                }
            }
605

Tebjan Halm's avatar
Tebjan Halm committed
606
            
607
            //events
Tebjan Halm's avatar
Tebjan Halm committed
608
            if(AutoPublishEvents)
609
            {
610
611
612
	            foreach (var attr in _svgEventAttributes)
	            {
	                var evt = attr.Event.GetValue(this);
Eric Domke's avatar
Eric Domke committed
613

614
	                //if someone has registered publish the attribute
Tebjan Halm's avatar
Tebjan Halm committed
615
	                if (evt != null && !string.IsNullOrEmpty(this.ID))
616
617
618
619
	                {
	                    writer.WriteAttributeString(attr.Attribute.Name, this.ID + "/" + attr.Attribute.Name);
	                }
	            }
620
621
            }

622
623
624
625
626
            //add the custom attributes
            foreach (var item in this._customAttributes)
            {
                writer.WriteAttributeString(item.Key, item.Value);
            }
627
        }
Tebjan Halm's avatar
Tebjan Halm committed
628
629
        
        public bool AutoPublishEvents = true;
630

631
        private bool TryResolveParentAttributeValue(string attributeKey, out object parentAttributeValue)
632
        {
633
            parentAttributeValue = null;
634

tebjan's avatar
tebjan committed
635
            //attributeKey = char.ToUpper(attributeKey[0]) + attributeKey.Substring(1);
636
637

            var currentParent = Parent;
638
            var resolved = false;
639
640
641
642
            while (currentParent != null)
            {
                if (currentParent.Attributes.ContainsKey(attributeKey))
                {
643
644
645
                    resolved = true;
                    parentAttributeValue = currentParent.Attributes[attributeKey];
                    if (parentAttributeValue != null)
646
647
648
649
                        break;
                }
                currentParent = currentParent.Parent;
            }
650

651
            return resolved;
davescriven's avatar
davescriven committed
652
653
654
655
656
657
658
659
660
661
662
663
664
665
        }

        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
666
667
668
669
670
            //write the content
            if(!String.IsNullOrEmpty(this.Content))
                writer.WriteString(this.Content);

            //write all children
davescriven's avatar
davescriven committed
671
672
673
674
675
676
677
            foreach (SvgElement child in this.Children)
            {
                child.Write(writer);
            }
        }

        /// <summary>
Eric Domke's avatar
Eric Domke committed
678
        /// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="ISvgRenderer"/> object.
davescriven's avatar
davescriven committed
679
        /// </summary>
Eric Domke's avatar
Eric Domke committed
680
681
        /// <param name="renderer">The <see cref="ISvgRenderer"/> object to render to.</param>
        protected virtual void Render(ISvgRenderer renderer)
davescriven's avatar
davescriven committed
682
        {
683
684
685
            this.PushTransforms(renderer);
            this.RenderChildren(renderer);
            this.PopTransforms(renderer);
davescriven's avatar
davescriven committed
686
687
        }

688
689
690
        /// <summary>
        /// Renders the children of this <see cref="SvgElement"/>.
        /// </summary>
Eric Domke's avatar
Eric Domke committed
691
692
        /// <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
693
694
695
        {
            foreach (SvgElement element in this.Children)
            {
696
                element.Render(renderer);
davescriven's avatar
davescriven committed
697
698
699
            }
        }

700
        /// <summary>
Eric Domke's avatar
Eric Domke committed
701
        /// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="ISvgRenderer"/> object.
702
        /// </summary>
Eric Domke's avatar
Eric Domke committed
703
704
        /// <param name="renderer">The <see cref="ISvgRenderer"/> object to render to.</param>
        void ISvgElement.Render(ISvgRenderer renderer)
davescriven's avatar
davescriven committed
705
        {
706
            this.Render(renderer);
davescriven's avatar
davescriven committed
707
        }
Tebjan Halm's avatar
Tebjan Halm committed
708
709
710
711
712
713
714
715
        
        /// <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
716
        	foreach(var child in elem.Children)
Tebjan Halm's avatar
Tebjan Halm committed
717
        	{
Tebjan Halm's avatar
Tebjan Halm committed
718
        		if (child is SvgVisualElement)
Tebjan Halm's avatar
Tebjan Halm committed
719
        		{
Tebjan Halm's avatar
Tebjan Halm committed
720
        			if(!(child is SvgGroup))
721
        			{
722
        				var childPath = ((SvgVisualElement)child).Path(null);
723
        				
Tebjan Halm's avatar
Tebjan Halm committed
724
725
726
727
728
        				if (childPath != null)
        				{
        					childPath = (GraphicsPath)childPath.Clone();
        					if(child.Transforms != null)
        						childPath.Transform(child.Transforms.GetMatrix());
729
730

                            if (childPath.PointCount > 0) path.AddPath(childPath, false);
Tebjan Halm's avatar
Tebjan Halm committed
731
        				}
732
        			}
Tebjan Halm's avatar
Tebjan Halm committed
733
        		}
734
735

                if (!(child is SvgPaintServer)) AddPaths(child, path);
Tebjan Halm's avatar
Tebjan Halm committed
736
        	}
Tebjan Halm's avatar
Tebjan Halm committed
737
738
739
740
741
742
743
        }
        
        /// <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
744
        protected GraphicsPath GetPaths(SvgElement elem, ISvgRenderer renderer)
Tebjan Halm's avatar
Tebjan Halm committed
745
746
747
748
749
750
751
752
753
        {
        	var ret = new GraphicsPath();
        	
        	foreach(var child in elem.Children)
        	{
        		if (child is SvgVisualElement)
        		{
        			if(!(child is SvgGroup))
        			{
754
                        var childPath = ((SvgVisualElement)child).Path(renderer);
Tebjan Halm's avatar
Tebjan Halm committed
755
756
757
758
759
760
761
762
763
764
765
766
        				
        				if (childPath != null)
        				{
        					childPath = (GraphicsPath)childPath.Clone();
        					if(child.Transforms != null)
        						childPath.Transform(child.Transforms.GetMatrix());
        					
        					ret.AddPath(childPath, false);
        				}
        			}
        			else
        			{
767
                        var childPath = GetPaths(child, renderer);
Tebjan Halm's avatar
Tebjan Halm committed
768
769
770
771
772
773
        				if(child.Transforms != null)
        					childPath.Transform(child.Transforms.GetMatrix());
        			}
        		}
        			
        	}
Tebjan Halm's avatar
Tebjan Halm committed
774
        	
Tebjan Halm's avatar
Tebjan Halm committed
775
        	return ret;
Tebjan Halm's avatar
Tebjan Halm committed
776
        }
davescriven's avatar
davescriven committed
777

778
779
780
781
782
783
        /// <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
784
785
786
787
        public virtual object Clone()
        {
            return this.MemberwiseClone();
        }
788
789
790
791
792
793

    	public abstract SvgElement DeepCopy();

		public virtual SvgElement DeepCopy<T>() where T : SvgElement, new()
		{
			var newObj = new T();
794
			newObj.ID = this.ID;
795
796
			newObj.Content = this.Content;
			newObj.ElementName = this.ElementName;
Eric Domke's avatar
Eric Domke committed
797

798
799
800
801
802
//			if (this.Parent != null)
	//			this.Parent.Children.Add(newObj);

			if (this.Transforms != null)
			{
803
				newObj.Transforms = this.Transforms.Clone() as SvgTransformCollection;
804
805
806
807
808
809
			}

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

811
812
813
			foreach (var attr in this._svgEventAttributes)
			{
				var evt = attr.Event.GetValue(this);
Eric Domke's avatar
Eric Domke committed
814

815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
				//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
830
831
					else if (attr.Event.Name == "Click")
						newObj.Click += delegate {  };
832
833
					else if (attr.Event.Name == "Change") //text element
						(newObj as SvgText).Change += delegate {  };
834
835
				}
			}
Eric Domke's avatar
Eric Domke committed
836

837
838
839
840
841
842
843
			if(this._customAttributes.Count > 0)
			{
				foreach (var element in _customAttributes) 
				{
					newObj.CustomAttributes.Add(element.Key, element.Value);
				}
			}
Eric Domke's avatar
Eric Domke committed
844

845
			return newObj;
Tebjan Halm's avatar
Tebjan Halm committed
846
        }
Eric Domke's avatar
Eric Domke committed
847

848
849
850
851
		/// <summary>
        /// Fired when an Atrribute of this Element has changed
        /// </summary>
		public event EventHandler<AttributeEventArgs> AttributeChanged;
Eric Domke's avatar
Eric Domke committed
852

853
854
855
856
857
858
859
860
		protected void OnAttributeChanged(AttributeEventArgs args)
		{
			var handler = AttributeChanged;
			if(handler != null)
			{
				handler(this, args);
			}
		}
Eric Domke's avatar
Eric Domke committed
861

tebjan's avatar
tebjan committed
862
863
864
865
		/// <summary>
        /// Fired when an Atrribute of this Element has changed
        /// </summary>
		public event EventHandler<ContentEventArgs> ContentChanged;
Eric Domke's avatar
Eric Domke committed
866

tebjan's avatar
tebjan committed
867
868
869
870
871
872
873
874
		protected void OnContentChanged(ContentEventArgs args)
		{
			var handler = ContentChanged;
			if(handler != null)
			{
				handler(this, args);
			}
		}
Tebjan Halm's avatar
Tebjan Halm committed
875
876
877
878

        #region graphical EVENTS

        /*  
879
880
881
            	onfocusin = "<anything>"
            	onfocusout = "<anything>"
            	onactivate = "<anything>"
882
883
884
885
886
                onclick = "<anything>"
                onmousedown = "<anything>"
                onmouseup = "<anything>"
                onmouseover = "<anything>"
                onmousemove = "<anything>"
887
            	onmouseout = "<anything>" 
Tebjan Halm's avatar
Tebjan Halm committed
888
889
         */

Eric Domke's avatar
Eric Domke committed
890
#if Net4
891
892
893
        /// <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.
894
        /// The SvgTextElement overwrites this and regsiters the Change event tor its text content.
895
896
        /// </summary>
        /// <param name="caller"></param>
897
        public virtual void RegisterEvents(ISvgEventCaller caller)
898
        {
Tebjan Halm's avatar
Tebjan Halm committed
899
            if (caller != null && !string.IsNullOrEmpty(this.ID))
900
901
902
            {
                var rpcID = this.ID + "/";

903
904
905
906
907
908
                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));
909
                caller.RegisterAction<int, bool, bool, bool, string>(rpcID + "onmousescroll", OnMouseScroll);
910
911
            }
        }
912
913
914
915
916
        
        /// <summary>
        /// Use this method to provide your implementation ISvgEventCaller to unregister Actions
        /// </summary>
        /// <param name="caller"></param>
917
        public virtual void UnregisterEvents(ISvgEventCaller caller)
918
        {
Tebjan Halm's avatar
Tebjan Halm committed
919
        	if (caller != null && !string.IsNullOrEmpty(this.ID))
920
921
922
923
924
925
926
        	{
        		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
927
        		caller.UnregisterAction(rpcID + "onmousescroll");
928
929
930
931
        		caller.UnregisterAction(rpcID + "onmouseover");
        		caller.UnregisterAction(rpcID + "onmouseout");
        	}
        }
Eric Domke's avatar
Eric Domke committed
932
#endif
933

Tebjan Halm's avatar
Tebjan Halm committed
934
935
936
        [SvgAttribute("onclick")]
        public event EventHandler<MouseArg> Click;

937
938
939
940
941
        [SvgAttribute("onmousedown")]
        public event EventHandler<MouseArg> MouseDown;

        [SvgAttribute("onmouseup")]
        public event EventHandler<MouseArg> MouseUp;
942
943
        
        [SvgAttribute("onmousemove")]
944
        public event EventHandler<MouseArg> MouseMove;
945

joreg's avatar
joreg committed
946
        [SvgAttribute("onmousescroll")]
947
        public event EventHandler<MouseScrollArg> MouseScroll;
joreg's avatar
joreg committed
948
        
949
        [SvgAttribute("onmouseover")]
950
        public event EventHandler<MouseArg> MouseOver;
951
952

        [SvgAttribute("onmouseout")]
953
        public event EventHandler<MouseArg> MouseOut;
954
        
Eric Domke's avatar
Eric Domke committed
955
#if Net4
956
        protected Action<float, float, int, int, bool, bool, bool, string> CreateMouseEventAction(Action<object, MouseArg> eventRaiser)
Tebjan Halm's avatar
Tebjan Halm committed
957
        {
958
959
        	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 });
960
        }
Eric Domke's avatar
Eric Domke committed
961
962
#endif

963
        //click
964
965
966
967
968
969
        protected void RaiseMouseClick(object sender, MouseArg e)
        {
        	var handler = Click;
        	if (handler != null)
        	{
        		handler(sender, e);
970
971
972
            }
        }

973
        //down
Tebjan Halm's avatar
Tebjan Halm committed
974
975
976
        protected void RaiseMouseDown(object sender, MouseArg e)
        {
        	var handler = MouseDown;
977
978
            if (handler != null)
            {
Tebjan Halm's avatar
Tebjan Halm committed
979
                handler(sender, e);
980
981
982
            }
        }

983
984
985
986
        //up
        protected void RaiseMouseUp(object sender, MouseArg e)
        {
        	var handler = MouseUp;
987
988
            if (handler != null)
            {
989
                handler(sender, e);
990
991
            }
        }
992
993

        protected void RaiseMouseMove(object sender, MouseArg e)
994
995
        {
        	var handler = MouseMove;
996
997
            if (handler != null)
            {
998
                handler(sender, e);
999
1000
            }
        }