SvgElement.cs 42.7 KB
Newer Older
1
using System;
davescriven's avatar
davescriven committed
2
3
4
5
6
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

        public bool ContainsAttribute(string name)
        {
            SortedDictionary<int, string> rules;
            return (this.Attributes.ContainsKey(name) || this.CustomAttributes.ContainsKey(name) ||
80
                (_styles != null && _styles.TryGetValue(name, out rules)) && (rules.ContainsKey(StyleSpecificity_InlineStyle) || rules.ContainsKey(StyleSpecificity_PresAttribute)));
Eric Domke's avatar
Eric Domke committed
81
82
83
84
85
86
87
88
89
90
91
        }
        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;
92
            if (_styles != null && _styles.TryGetValue(name, out rules))
Eric Domke's avatar
Eric Domke committed
93
94
95
96
97
98
99
100
            {
                // 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
        /// <summary>
        /// Gets or sets the color <see cref="SvgPaintServer"/> of this element which drives the currentColor property.
        /// </summary>
Eric Domke's avatar
Eric Domke committed
126
        [SvgAttribute("color", true)]
127
128
129
130
131
132
        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
        }

        /// <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>
178
        /// Gets a collection of all child <see cref="SvgElement"/> objects.
davescriven's avatar
davescriven committed
179
180
181
182
183
184
        /// </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; }
        }

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

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
372
373
374
375
376
377
378
379
380
381

        /// <summary>
        /// Transforms the given rectangle with the set transformation, if any.
        /// Can be applied to bounds calculated without considering the element transformation. 
        /// </summary>
        /// <param name="bounds">The rectangle to be transformed.</param>
        /// <returns>The transformed rectangle, or the original rectangle if no transformation exists.</returns>
        protected RectangleF TransformedBounds(RectangleF bounds)
        {
            if (Transforms != null && Transforms.Count > 0)
            {
                var path = new GraphicsPath();
                path.AddRectangle(bounds);
                path.Transform(Transforms.GetMatrix());
                return path.GetBounds();
            }
            return bounds;
        }
davescriven's avatar
davescriven committed
382
383
384
385
386
387
388
389

        /// <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
        {
390
            get { return this.Attributes.GetAttribute<string>("id"); }
davescriven's avatar
davescriven committed
391
392
            set
            {
tebjan's avatar
tebjan committed
393
                SetAndForceUniqueID(value, false);
394
395
            }
        }
davescriven's avatar
davescriven committed
396

Eric Domke's avatar
Eric Domke committed
397
        /// <summary>
398
        /// Gets or sets the space handling.
Eric Domke's avatar
Eric Domke committed
399
        /// </summary>
400
        /// <value>The space handling.</value>
Eric Domke's avatar
Eric Domke committed
401
402
403
        [SvgAttribute("space", SvgAttributeAttribute.XmlNamespace)]
        public virtual XmlSpaceHandling SpaceHandling
        {
tebjan's avatar
tebjan committed
404
            get { return (this.Attributes["space"] == null) ? XmlSpaceHandling.@default : (XmlSpaceHandling)this.Attributes["space"]; }
Eric Domke's avatar
Eric Domke committed
405
406
407
            set { this.Attributes["space"] = value; }
        }

tebjan's avatar
tebjan committed
408
        public void SetAndForceUniqueID(string value, bool autoForceUniqueID = true, Action<SvgElement, string, string> logElementOldIDNewID = null)
409
410
411
412
413
414
        {
            // Don't do anything if it hasn't changed
            if (string.Compare(this.ID, value) == 0)
            {
                return;
            }
davescriven's avatar
davescriven committed
415

416
417
418
419
            if (this.OwnerDocument != null)
            {
                this.OwnerDocument.IdManager.Remove(this);
            }
davescriven's avatar
davescriven committed
420

421
            this.Attributes["id"] = value;
422
423
424

            if (this.OwnerDocument != null)
            {
tebjan's avatar
tebjan committed
425
                this.OwnerDocument.IdManager.AddAndForceUniqueID(this, null, autoForceUniqueID, logElementOldIDNewID);
davescriven's avatar
davescriven committed
426
427
428
            }
        }

429
430
431
432
        /// <summary>
        /// Only used by the ID Manager
        /// </summary>
        /// <param name="newID"></param>
tebjan's avatar
tebjan committed
433
        internal void ForceUniqueID(string newID)
434
        {
435
            this.Attributes["id"] = newID;
436
437
        }

438
439
440
441
442
443
        /// <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>
444
        protected virtual void AddElement(SvgElement child, int index)
davescriven's avatar
davescriven committed
445
446
        {
        }
tebjan's avatar
tebjan committed
447
448
449
450
451
        
        /// <summary>
        /// Fired when an Element was added to the children of this Element
        /// </summary>
		public event EventHandler<ChildAddedEventArgs> ChildAdded;
davescriven's avatar
davescriven committed
452

453
454
455
456
457
        /// <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
458
459
        internal void OnElementAdded(SvgElement child, int index)
        {
460
            this.AddElement(child, index);
461
462
463
464
465
            SvgElement sibling = null;
            if(index < (Children.Count - 1))
            {
            	sibling = Children[index + 1];
            }
tebjan's avatar
tebjan committed
466
467
468
            var handler = ChildAdded;
            if(handler != null)
            {
469
            	handler(this, new ChildAddedEventArgs { NewChild = child, BeforeSibling = sibling });
tebjan's avatar
tebjan committed
470
            }
davescriven's avatar
davescriven committed
471
472
        }

473
474
475
476
477
        /// <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>
478
        protected virtual void RemoveElement(SvgElement child)
davescriven's avatar
davescriven committed
479
480
481
        {
        }

482
483
484
485
        /// <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
486
487
        internal void OnElementRemoved(SvgElement child)
        {
488
            this.RemoveElement(child);
davescriven's avatar
davescriven committed
489
490
491
492
493
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SvgElement"/> class.
        /// </summary>
494
        public SvgElement()
davescriven's avatar
davescriven committed
495
496
497
498
        {
            this._children = new SvgElementCollection(this);
            this._eventHandlers = new EventHandlerList();
            this._elementName = string.Empty;
499
500
            this._customAttributes = new SvgCustomAttributeCollection(this);
            
Tebjan Halm's avatar
Tebjan Halm committed
501
502
            Transforms = new SvgTransformCollection();
            
503
504
505
            //subscribe to attribute events
            Attributes.AttributeChanged += Attributes_AttributeChanged;
            CustomAttributes.AttributeChanged += Attributes_AttributeChanged;
506

507
508
509
510
511
            //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
512

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

518
        }
519

520
521
522
523
524
525
        //dispatch attribute event
        void Attributes_AttributeChanged(object sender, AttributeEventArgs e)
        {
        	OnAttributeChanged(e);
        }

526
527
		public virtual void InitialiseFromXML(XmlTextReader reader, SvgDocument document)
		{
528
            throw new NotImplementedException();
529
530
531
		}


532
        /// <summary>
Eric Domke's avatar
Eric Domke committed
533
        /// Renders this element to the <see cref="ISvgRenderer"/>.
534
        /// </summary>
Eric Domke's avatar
Eric Domke committed
535
536
        /// <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
537
        {
538
            this.Render(renderer);
davescriven's avatar
davescriven committed
539
540
        }

541
542
        /// <summary>Derrived classes may decide that the element should not be written. For example, the text element shouldn't be written if it's empty.</summary>
        public virtual bool ShouldWriteElement()
davescriven's avatar
davescriven committed
543
        {
544
545
            //Write any element who has a name.
            return (this.ElementName != String.Empty);
davescriven's avatar
davescriven committed
546
547
548
549
550
551
552
553
        }

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

davescriven's avatar
davescriven committed
555
556
557
558
559
560
561
562
563
564
565
566
            this.WriteAttributes(writer);
        }

        protected virtual void WriteEndElement(XmlTextWriter writer)
        {
            if (this.ElementName != String.Empty)
            {
                writer.WriteEndElement();
            }
        }
        protected virtual void WriteAttributes(XmlTextWriter writer)
        {
Eric Domke's avatar
Eric Domke committed
567
568
569
570
            var styles = new Dictionary<string, string>();
            bool writeStyle;
            bool forceWrite;

571
572
            //properties
            foreach (var attr in _svgPropertyAttributes)
573
            {
Eric Domke's avatar
Eric Domke committed
574
575
                if (attr.Property.Converter.CanConvertTo(typeof(string)) && 
                    (!attr.Attribute.InAttributeDictionary || _attributes.ContainsKey(attr.Attribute.Name)))
576
577
                {
                    object propertyValue = attr.Property.GetValue(this);
578
                    string value = (string)attr.Property.Converter.ConvertTo(propertyValue, typeof(string));
579

Eric Domke's avatar
Eric Domke committed
580
581
                    forceWrite = false;
                    writeStyle = (attr.Attribute.Name == "fill");
582

583
                    if (writeStyle && (Parent != null))
584
                    {
585
                    	if(propertyValue == SvgColourServer.NotSet) continue;
586
                    	
587
588
                        object parentValue;
                        if (TryResolveParentAttributeValue(attr.Attribute.Name, out parentValue))
589
                        {
590
591
                            if ((parentValue == propertyValue) 
                                || ((parentValue != null) &&  parentValue.Equals(propertyValue)))
592
                                continue;
593
                            
594
595
596
597
                            forceWrite = true;
                        }
                    }

598
599
                    if (propertyValue != null)
                    {
Tebjan Halm's avatar
Tebjan Halm committed
600
                        var type = propertyValue.GetType();
601
602
603
                        
                        //Only write the attribute's value if it is not the default value, not null/empty, or we're forcing the write.
                        if ((!string.IsNullOrEmpty(value) && !SvgDefaults.IsDefault(attr.Attribute.Name, value)) || forceWrite)
Tebjan Halm's avatar
Tebjan Halm committed
604
                        {
Eric Domke's avatar
Eric Domke committed
605
606
607
608
609
610
611
612
                            if (writeStyle)
                            {
                                styles[attr.Attribute.Name] = value;
                            }
                            else
                            {
                                writer.WriteAttributeString(attr.Attribute.NamespaceAndName, value);
                            }
Tebjan Halm's avatar
Tebjan Halm committed
613
614
615
616
                        }
                    }
                    else if(attr.Attribute.Name == "fill") //if fill equals null, write 'none'
                    {
Eric Domke's avatar
Eric Domke committed
617
618
619
620
621
622
623
624
                        if (writeStyle)
                        {
                            styles[attr.Attribute.Name] = value;
                        }
                        else
                        {
                            writer.WriteAttributeString(attr.Attribute.NamespaceAndName, value);
                        }
625
626
627
                    }
                }
            }
628

629
            //events
Tebjan Halm's avatar
Tebjan Halm committed
630
            if(AutoPublishEvents)
631
            {
632
633
634
	            foreach (var attr in _svgEventAttributes)
	            {
	                var evt = attr.Event.GetValue(this);
Eric Domke's avatar
Eric Domke committed
635

636
	                //if someone has registered publish the attribute
Tebjan Halm's avatar
Tebjan Halm committed
637
	                if (evt != null && !string.IsNullOrEmpty(this.ID))
638
639
640
641
	                {
	                    writer.WriteAttributeString(attr.Attribute.Name, this.ID + "/" + attr.Attribute.Name);
	                }
	            }
642
643
            }

644
645
646
647
648
            //add the custom attributes
            foreach (var item in this._customAttributes)
            {
                writer.WriteAttributeString(item.Key, item.Value);
            }
Eric Domke's avatar
Eric Domke committed
649
650
651
652
653
654
655

            //write the style property
            if (styles.Any())
            {
                writer.WriteAttributeString("style", (from s in styles
                                                      select s.Key + ":" + s.Value + ";").Aggregate((p,c) => p + c));
            }
656
        }
Tebjan Halm's avatar
Tebjan Halm committed
657
658
        
        public bool AutoPublishEvents = true;
659

660
        private bool TryResolveParentAttributeValue(string attributeKey, out object parentAttributeValue)
661
        {
662
            parentAttributeValue = null;
663

tebjan's avatar
tebjan committed
664
            //attributeKey = char.ToUpper(attributeKey[0]) + attributeKey.Substring(1);
665
666

            var currentParent = Parent;
667
            var resolved = false;
668
669
670
671
            while (currentParent != null)
            {
                if (currentParent.Attributes.ContainsKey(attributeKey))
                {
672
673
674
                    resolved = true;
                    parentAttributeValue = currentParent.Attributes[attributeKey];
                    if (parentAttributeValue != null)
675
676
677
678
                        break;
                }
                currentParent = currentParent.Parent;
            }
679

680
            return resolved;
davescriven's avatar
davescriven committed
681
682
        }

683
        public virtual void Write(XmlTextWriter writer)
davescriven's avatar
davescriven committed
684
        {
685
            if (ShouldWriteElement())
davescriven's avatar
davescriven committed
686
687
688
689
690
691
692
693
694
            {
                this.WriteStartElement(writer);
                this.WriteChildren(writer);
                this.WriteEndElement(writer);
            }
        }

        protected virtual void WriteChildren(XmlTextWriter writer)
        {
Eric Domke's avatar
Eric Domke committed
695
            if (this.Nodes.Any())
davescriven's avatar
davescriven committed
696
            {
Eric Domke's avatar
Eric Domke committed
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
                SvgContentNode content;
                foreach (var node in this.Nodes)
                {
                    content = node as SvgContentNode;
                    if (content == null)
                    {
                        ((SvgElement)node).Write(writer);
                    }
                    else if (!string.IsNullOrEmpty(content.Content))
                    {
                        writer.WriteString(content.Content);
                    }
                }
            }
            else
            {
                //write the content
                if(!String.IsNullOrEmpty(this.Content))
                    writer.WriteString(this.Content);

                //write all children
                foreach (SvgElement child in this.Children)
                {
                    child.Write(writer);
                }
davescriven's avatar
davescriven committed
722
723
724
725
            }
        }

        /// <summary>
Eric Domke's avatar
Eric Domke committed
726
        /// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="ISvgRenderer"/> object.
davescriven's avatar
davescriven committed
727
        /// </summary>
Eric Domke's avatar
Eric Domke committed
728
729
        /// <param name="renderer">The <see cref="ISvgRenderer"/> object to render to.</param>
        protected virtual void Render(ISvgRenderer renderer)
davescriven's avatar
davescriven committed
730
        {
731
732
733
            this.PushTransforms(renderer);
            this.RenderChildren(renderer);
            this.PopTransforms(renderer);
davescriven's avatar
davescriven committed
734
735
        }

736
737
738
        /// <summary>
        /// Renders the children of this <see cref="SvgElement"/>.
        /// </summary>
Eric Domke's avatar
Eric Domke committed
739
740
        /// <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
741
742
743
        {
            foreach (SvgElement element in this.Children)
            {
744
                element.Render(renderer);
davescriven's avatar
davescriven committed
745
746
747
            }
        }

748
        /// <summary>
Eric Domke's avatar
Eric Domke committed
749
        /// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="ISvgRenderer"/> object.
750
        /// </summary>
Eric Domke's avatar
Eric Domke committed
751
752
        /// <param name="renderer">The <see cref="ISvgRenderer"/> object to render to.</param>
        void ISvgElement.Render(ISvgRenderer renderer)
davescriven's avatar
davescriven committed
753
        {
754
            this.Render(renderer);
davescriven's avatar
davescriven committed
755
        }
Tebjan Halm's avatar
Tebjan Halm committed
756
757
758
759
760
761
762
763
        
        /// <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
764
        	foreach(var child in elem.Children)
Tebjan Halm's avatar
Tebjan Halm committed
765
        	{
766
767
768
769
770
771
            // Skip to avoid double calculate Symbol element
            // symbol element is only referenced by use element 
            // So here we need to skip when it is directly considered
            if (child is Svg.Document_Structure.SvgSymbol)
              continue;

Tebjan Halm's avatar
Tebjan Halm committed
772
        		if (child is SvgVisualElement)
Tebjan Halm's avatar
Tebjan Halm committed
773
        		{
Tebjan Halm's avatar
Tebjan Halm committed
774
        			if(!(child is SvgGroup))
775
        			{
776
        				var childPath = ((SvgVisualElement)child).Path(null);
777
        				
Tebjan Halm's avatar
Tebjan Halm committed
778
779
780
781
782
        				if (childPath != null)
        				{
        					childPath = (GraphicsPath)childPath.Clone();
        					if(child.Transforms != null)
        						childPath.Transform(child.Transforms.GetMatrix());
783
784

                            if (childPath.PointCount > 0) path.AddPath(childPath, false);
Tebjan Halm's avatar
Tebjan Halm committed
785
        				}
786
        			}
Tebjan Halm's avatar
Tebjan Halm committed
787
        		}
788
789

                if (!(child is SvgPaintServer)) AddPaths(child, path);
Tebjan Halm's avatar
Tebjan Halm committed
790
        	}
Tebjan Halm's avatar
Tebjan Halm committed
791
792
793
794
795
796
        }
        
        /// <summary>
        /// Recursive method to add up the paths of all children
        /// </summary>
        /// <param name="elem"></param>
797
        /// <param name="renderer"></param>
Eric Domke's avatar
Eric Domke committed
798
        protected GraphicsPath GetPaths(SvgElement elem, ISvgRenderer renderer)
Tebjan Halm's avatar
Tebjan Halm committed
799
800
801
802
803
804
805
806
807
        {
        	var ret = new GraphicsPath();
        	
        	foreach(var child in elem.Children)
        	{
        		if (child is SvgVisualElement)
        		{
        			if(!(child is SvgGroup))
        			{
808
                        var childPath = ((SvgVisualElement)child).Path(renderer);
Tebjan Halm's avatar
Tebjan Halm committed
809
        				
810
811
812
813
      					// Non-group element can have child element which we have to consider. i.e tspan in text element
      					if (child.Children.Count > 0)
    				  		childPath.AddPath(GetPaths(child, renderer), false);

814
        				if (childPath != null && childPath.PointCount > 0)
Tebjan Halm's avatar
Tebjan Halm committed
815
816
817
818
819
820
821
822
823
824
        				{
        					childPath = (GraphicsPath)childPath.Clone();
        					if(child.Transforms != null)
        						childPath.Transform(child.Transforms.GetMatrix());
        					
        					ret.AddPath(childPath, false);
        				}
        			}
        			else
        			{
825
826
827
828
829
830
831
832
				        var childPath = GetPaths(child, renderer);
        				if (childPath != null && childPath.PointCount > 0)
        				{
        					if (child.Transforms != null)
						        childPath.Transform(child.Transforms.GetMatrix());
                  
					        ret.AddPath(childPath, false);
				        }
Tebjan Halm's avatar
Tebjan Halm committed
833
834
835
836
        			}
        		}
        			
        	}
Tebjan Halm's avatar
Tebjan Halm committed
837
        	
Tebjan Halm's avatar
Tebjan Halm committed
838
        	return ret;
Tebjan Halm's avatar
Tebjan Halm committed
839
        }
davescriven's avatar
davescriven committed
840

841
842
843
844
845
846
        /// <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
847
848
849
850
        public virtual object Clone()
        {
            return this.MemberwiseClone();
        }
851
852
853

    	public abstract SvgElement DeepCopy();

854
855
856
857
858
        ISvgNode ISvgNode.DeepCopy()
        {
            return DeepCopy();
        }

859
860
861
		public virtual SvgElement DeepCopy<T>() where T : SvgElement, new()
		{
			var newObj = new T();
862
			newObj.ID = this.ID;
863
864
			newObj.Content = this.Content;
			newObj.ElementName = this.ElementName;
Eric Domke's avatar
Eric Domke committed
865

866
867
868
869
870
//			if (this.Parent != null)
	//			this.Parent.Children.Add(newObj);

			if (this.Transforms != null)
			{
871
				newObj.Transforms = this.Transforms.Clone() as SvgTransformCollection;
872
873
874
875
876
877
			}

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

879
880
881
			foreach (var attr in this._svgEventAttributes)
			{
				var evt = attr.Event.GetValue(this);
Eric Domke's avatar
Eric Domke committed
882

883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
				//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
898
899
					else if (attr.Event.Name == "Click")
						newObj.Click += delegate {  };
900
901
					else if (attr.Event.Name == "Change") //text element
						(newObj as SvgText).Change += delegate {  };
902
903
				}
			}
Eric Domke's avatar
Eric Domke committed
904

905
906
907
908
909
910
911
			if(this._customAttributes.Count > 0)
			{
				foreach (var element in _customAttributes) 
				{
					newObj.CustomAttributes.Add(element.Key, element.Value);
				}
			}
Eric Domke's avatar
Eric Domke committed
912

913
914
915
916
917
918
919
920
            if (this._nodes.Count > 0)
            {
                foreach (var node in this._nodes)
                {
                    newObj.Nodes.Add(node.DeepCopy());
                }
            }
            return newObj;
Tebjan Halm's avatar
Tebjan Halm committed
921
        }
Eric Domke's avatar
Eric Domke committed
922

923
924
925
926
		/// <summary>
        /// Fired when an Atrribute of this Element has changed
        /// </summary>
		public event EventHandler<AttributeEventArgs> AttributeChanged;
Eric Domke's avatar
Eric Domke committed
927

928
929
930
931
932
933
934
935
		protected void OnAttributeChanged(AttributeEventArgs args)
		{
			var handler = AttributeChanged;
			if(handler != null)
			{
				handler(this, args);
			}
		}
Eric Domke's avatar
Eric Domke committed
936

tebjan's avatar
tebjan committed
937
938
939
940
		/// <summary>
        /// Fired when an Atrribute of this Element has changed
        /// </summary>
		public event EventHandler<ContentEventArgs> ContentChanged;
Eric Domke's avatar
Eric Domke committed
941

tebjan's avatar
tebjan committed
942
943
944
945
946
947
948
949
		protected void OnContentChanged(ContentEventArgs args)
		{
			var handler = ContentChanged;
			if(handler != null)
			{
				handler(this, args);
			}
		}
Tebjan Halm's avatar
Tebjan Halm committed
950
951
952
953

        #region graphical EVENTS

        /*  
954
955
956
            	onfocusin = "<anything>"
            	onfocusout = "<anything>"
            	onactivate = "<anything>"
957
958
959
960
961
                onclick = "<anything>"
                onmousedown = "<anything>"
                onmouseup = "<anything>"
                onmouseover = "<anything>"
                onmousemove = "<anything>"
962
            	onmouseout = "<anything>" 
Tebjan Halm's avatar
Tebjan Halm committed
963
964
         */

Eric Domke's avatar
Eric Domke committed
965
#if Net4
966
967
968
        /// <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.
969
        /// The SvgTextElement overwrites this and regsiters the Change event tor its text content.
970
971
        /// </summary>
        /// <param name="caller"></param>
972
        public virtual void RegisterEvents(ISvgEventCaller caller)
973
        {
Tebjan Halm's avatar
Tebjan Halm committed
974
            if (caller != null && !string.IsNullOrEmpty(this.ID))
975
976
977
            {
                var rpcID = this.ID + "/";

978
979
980
981
982
983
                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));
984
                caller.RegisterAction<int, bool, bool, bool, string>(rpcID + "onmousescroll", OnMouseScroll);
985
986
            }
        }
987
988
989
990
991
        
        /// <summary>
        /// Use this method to provide your implementation ISvgEventCaller to unregister Actions
        /// </summary>
        /// <param name="caller"></param>
992
        public virtual void UnregisterEvents(ISvgEventCaller caller)
993
        {
Tebjan Halm's avatar
Tebjan Halm committed
994
        	if (caller != null && !string.IsNullOrEmpty(this.ID))
995
996
997
998
999
1000
        	{
        		var rpcID = this.ID + "/";

        		caller.UnregisterAction(rpcID + "onclick");
        		caller.UnregisterAction(rpcID + "onmousedown");
        		caller.UnregisterAction(rpcID + "onmouseup");