SvgElement.cs 43.1 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
        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()
        {
68
            if (_styles.Any())
Eric Domke's avatar
Eric Domke committed
69
            {
70
71
72
73
74
75
76
77
78
                var styles = new Dictionary<string, SortedDictionary<int, string>>();
                foreach (var s in _styles)
                {
                    if (!SvgElementFactory.SetPropertyValue(this, s.Key, s.Value.Last().Value, this.OwnerDocument, isStyle: true))
                    {
                        styles.Add(s.Key, s.Value);
                    }
                }
                _styles = styles;
Eric Domke's avatar
Eric Domke committed
79
80
81
            }
        }

Eric Domke's avatar
Eric Domke committed
82
83
84
85
86

        public bool ContainsAttribute(string name)
        {
            SortedDictionary<int, string> rules;
            return (this.Attributes.ContainsKey(name) || this.CustomAttributes.ContainsKey(name) ||
87
                (_styles.TryGetValue(name, out rules)) && (rules.ContainsKey(StyleSpecificity_InlineStyle) || rules.ContainsKey(StyleSpecificity_PresAttribute)));
Eric Domke's avatar
Eric Domke committed
88
89
90
91
92
93
94
95
96
97
98
        }
        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;
99
            if (_styles.TryGetValue(name, out rules))
Eric Domke's avatar
Eric Domke committed
100
101
102
103
104
105
106
107
            {
                // 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
108
109
110
        /// <summary>
        /// Gets the name of the element.
        /// </summary>
111
        protected internal string ElementName
davescriven's avatar
davescriven committed
112
        {
113
114
115
116
117
118
119
120
121
122
123
124
125
126
            get
            {
                if (string.IsNullOrEmpty(this._elementName))
                {
                    var attr = TypeDescriptor.GetAttributes(this).OfType<SvgElementAttribute>().SingleOrDefault();

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

                return this._elementName;
            }
127
            internal set { this._elementName = value; }
davescriven's avatar
davescriven committed
128
129
        }

130
131
132
        /// <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
133
        [SvgAttribute("color", true)]
134
135
136
137
138
139
        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
140
141
142
        /// <summary>
        /// Gets or sets the content of the element.
        /// </summary>
Tebjan Halm's avatar
Tebjan Halm committed
143
        private string _content;
davescriven's avatar
davescriven committed
144
145
        public virtual string Content
        {
Tebjan Halm's avatar
Tebjan Halm committed
146
147
148
149
150
151
152
153
154
155
156
            get
            {
            	return _content;
            }
            set
            {
            	if(_content != null)
            	{
            		var oldVal = _content;
            		_content = value;
            		if(_content != oldVal)
tebjan's avatar
tebjan committed
157
            			OnContentChanged(new ContentEventArgs{ Content = value });
Tebjan Halm's avatar
Tebjan Halm committed
158
159
160
161
            	}
            	else
            	{
            		_content = value;
tebjan's avatar
tebjan committed
162
            		OnContentChanged(new ContentEventArgs{ Content = value });
Tebjan Halm's avatar
Tebjan Halm committed
163
164
            	}
            }
davescriven's avatar
davescriven committed
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>
185
        /// Gets a collection of all child <see cref="SvgElement"/> objects.
davescriven's avatar
davescriven committed
186
187
188
189
190
191
        /// </summary>
        public virtual SvgElementCollection Children
        {
            get { return this._children; }
        }

192
193
194
195
196
        public IList<ISvgNode> Nodes
        {
            get { return this._nodes; }
        }

Eric Domke's avatar
Eric Domke committed
197
198
199
200
201
202
203
204
205
        public IEnumerable<SvgElement> Descendants()
        {
            return this.AsEnumerable().Descendants();
        }
        private IEnumerable<SvgElement> AsEnumerable()
        {
            yield return this;
        }

davescriven's avatar
davescriven committed
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
        /// <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; }
        }

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

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

286
287
288
289
        /// <summary>
        /// Gets a collection of custom attributes
        /// </summary>
        public SvgCustomAttributeCollection CustomAttributes
290
291
292
293
        {
            get { return this._customAttributes; }
        }

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

296
        /// <summary>
Eric Domke's avatar
Eric Domke committed
297
        /// Applies the required transforms to <see cref="ISvgRenderer"/>.
298
        /// </summary>
Eric Domke's avatar
Eric Domke committed
299
300
        /// <param name="renderer">The <see cref="ISvgRenderer"/> to be transformed.</param>
        protected internal virtual bool PushTransforms(ISvgRenderer renderer)
davescriven's avatar
davescriven committed
301
        {
302
            _graphicsMatrix = renderer.Transform;
Eric Domke's avatar
Eric Domke committed
303
            _graphicsClip = renderer.GetClip();
304

davescriven's avatar
davescriven committed
305
306
307
            // Return if there are no transforms
            if (this.Transforms == null || this.Transforms.Count == 0)
            {
308
                return true;
davescriven's avatar
davescriven committed
309
            }
Eric Domke's avatar
Eric Domke committed
310
            if (this.Transforms.Count == 1 && this.Transforms[0].Matrix.Equals(_zeroMatrix)) return false;
davescriven's avatar
davescriven committed
311

Eric Domke's avatar
Eric Domke committed
312
            Matrix transformMatrix = renderer.Transform.Clone();
davescriven's avatar
davescriven committed
313
314
315

            foreach (SvgTransform transformation in this.Transforms)
            {
316
                transformMatrix.Multiply(transformation.Matrix);
davescriven's avatar
davescriven committed
317
318
            }

319
            renderer.Transform = transformMatrix;
320
321

            return true;
davescriven's avatar
davescriven committed
322
323
        }

324
        /// <summary>
Eric Domke's avatar
Eric Domke committed
325
        /// Removes any previously applied transforms from the specified <see cref="ISvgRenderer"/>.
326
        /// </summary>
Eric Domke's avatar
Eric Domke committed
327
328
        /// <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
329
        {
330
            renderer.Transform = _graphicsMatrix;
davescriven's avatar
davescriven committed
331
            _graphicsMatrix = null;
332
333
            renderer.SetClip(_graphicsClip);
            _graphicsClip = null;
davescriven's avatar
davescriven committed
334
335
        }

336
        /// <summary>
Eric Domke's avatar
Eric Domke committed
337
        /// Applies the required transforms to <see cref="ISvgRenderer"/>.
338
        /// </summary>
Eric Domke's avatar
Eric Domke committed
339
340
        /// <param name="renderer">The <see cref="ISvgRenderer"/> to be transformed.</param>
        void ISvgTransformable.PushTransforms(ISvgRenderer renderer)
davescriven's avatar
davescriven committed
341
        {
342
            this.PushTransforms(renderer);
davescriven's avatar
davescriven committed
343
344
        }

345
        /// <summary>
Eric Domke's avatar
Eric Domke committed
346
        /// Removes any previously applied transforms from the specified <see cref="ISvgRenderer"/>.
347
        /// </summary>
Eric Domke's avatar
Eric Domke committed
348
349
        /// <param name="renderer">The <see cref="ISvgRenderer"/> that should have transforms removed.</param>
        void ISvgTransformable.PopTransforms(ISvgRenderer renderer)
davescriven's avatar
davescriven committed
350
        {
351
            this.PopTransforms(renderer);
davescriven's avatar
davescriven committed
352
353
354
355
356
357
358
359
360
        }

        /// <summary>
        /// Gets or sets the element transforms.
        /// </summary>
        /// <value>The transforms.</value>
        [SvgAttribute("transform")]
        public SvgTransformCollection Transforms
        {
361
            get { return (this.Attributes.GetAttribute<SvgTransformCollection>("transform")); }
Tebjan Halm's avatar
Tebjan Halm committed
362
363
364
365
366
367
            set 
            { 
            	var old = this.Transforms;
            	if(old != null)
            		old.TransformChanged -= Attributes_AttributeChanged;
            	value.TransformChanged += Attributes_AttributeChanged;
368
            	this.Attributes["transform"] = value; 
Tebjan Halm's avatar
Tebjan Halm committed
369
            }
davescriven's avatar
davescriven committed
370
        }
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388

        /// <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
389
390
391
392
393
394
395
396

        /// <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
        {
397
            get { return this.Attributes.GetAttribute<string>("id"); }
davescriven's avatar
davescriven committed
398
399
            set
            {
tebjan's avatar
tebjan committed
400
                SetAndForceUniqueID(value, false);
401
402
            }
        }
davescriven's avatar
davescriven committed
403

Eric Domke's avatar
Eric Domke committed
404
        /// <summary>
405
        /// Gets or sets the space handling.
Eric Domke's avatar
Eric Domke committed
406
        /// </summary>
407
        /// <value>The space handling.</value>
Eric Domke's avatar
Eric Domke committed
408
409
410
        [SvgAttribute("space", SvgAttributeAttribute.XmlNamespace)]
        public virtual XmlSpaceHandling SpaceHandling
        {
tebjan's avatar
tebjan committed
411
            get { return (this.Attributes["space"] == null) ? XmlSpaceHandling.@default : (XmlSpaceHandling)this.Attributes["space"]; }
Eric Domke's avatar
Eric Domke committed
412
413
414
            set { this.Attributes["space"] = value; }
        }

tebjan's avatar
tebjan committed
415
        public void SetAndForceUniqueID(string value, bool autoForceUniqueID = true, Action<SvgElement, string, string> logElementOldIDNewID = null)
416
417
418
419
420
421
        {
            // Don't do anything if it hasn't changed
            if (string.Compare(this.ID, value) == 0)
            {
                return;
            }
davescriven's avatar
davescriven committed
422

423
424
425
426
            if (this.OwnerDocument != null)
            {
                this.OwnerDocument.IdManager.Remove(this);
            }
davescriven's avatar
davescriven committed
427

428
            this.Attributes["id"] = value;
429
430
431

            if (this.OwnerDocument != null)
            {
tebjan's avatar
tebjan committed
432
                this.OwnerDocument.IdManager.AddAndForceUniqueID(this, null, autoForceUniqueID, logElementOldIDNewID);
davescriven's avatar
davescriven committed
433
434
435
            }
        }

436
437
438
439
        /// <summary>
        /// Only used by the ID Manager
        /// </summary>
        /// <param name="newID"></param>
tebjan's avatar
tebjan committed
440
        internal void ForceUniqueID(string newID)
441
        {
442
            this.Attributes["id"] = newID;
443
444
        }

445
446
447
448
449
450
        /// <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>
451
        protected virtual void AddElement(SvgElement child, int index)
davescriven's avatar
davescriven committed
452
453
        {
        }
tebjan's avatar
tebjan committed
454
455
456
457
458
        
        /// <summary>
        /// Fired when an Element was added to the children of this Element
        /// </summary>
		public event EventHandler<ChildAddedEventArgs> ChildAdded;
davescriven's avatar
davescriven committed
459

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

480
481
482
483
484
        /// <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>
485
        protected virtual void RemoveElement(SvgElement child)
davescriven's avatar
davescriven committed
486
487
488
        {
        }

489
490
491
492
        /// <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
493
494
        internal void OnElementRemoved(SvgElement child)
        {
495
            this.RemoveElement(child);
davescriven's avatar
davescriven committed
496
497
498
499
500
        }

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

514
515
516
517
518
            //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
519

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

525
        }
526

527
528
529
530
531
532
        //dispatch attribute event
        void Attributes_AttributeChanged(object sender, AttributeEventArgs e)
        {
        	OnAttributeChanged(e);
        }

533
534
		public virtual void InitialiseFromXML(XmlTextReader reader, SvgDocument document)
		{
535
            throw new NotImplementedException();
536
537
538
		}


539
        /// <summary>
Eric Domke's avatar
Eric Domke committed
540
        /// Renders this element to the <see cref="ISvgRenderer"/>.
541
        /// </summary>
Eric Domke's avatar
Eric Domke committed
542
543
        /// <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
544
        {
545
            this.Render(renderer);
davescriven's avatar
davescriven committed
546
547
        }

548
549
        /// <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
550
        {
551
552
            //Write any element who has a name.
            return (this.ElementName != String.Empty);
davescriven's avatar
davescriven committed
553
554
555
556
557
558
559
560
        }

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

davescriven's avatar
davescriven committed
562
563
564
565
566
567
568
569
570
571
572
573
            this.WriteAttributes(writer);
        }

        protected virtual void WriteEndElement(XmlTextWriter writer)
        {
            if (this.ElementName != String.Empty)
            {
                writer.WriteEndElement();
            }
        }
        protected virtual void WriteAttributes(XmlTextWriter writer)
        {
574
            var styles = _styles.ToDictionary(_styles => _styles.Key, _styles => _styles.Value.Last().Value);
Eric Domke's avatar
Eric Domke committed
575
576
577
            bool writeStyle;
            bool forceWrite;

578
579
            //properties
            foreach (var attr in _svgPropertyAttributes)
580
            {
Eric Domke's avatar
Eric Domke committed
581
582
                if (attr.Property.Converter.CanConvertTo(typeof(string)) && 
                    (!attr.Attribute.InAttributeDictionary || _attributes.ContainsKey(attr.Attribute.Name)))
583
584
                {
                    object propertyValue = attr.Property.GetValue(this);
585
                    string value = (string)attr.Property.Converter.ConvertTo(propertyValue, typeof(string));
586

Eric Domke's avatar
Eric Domke committed
587
                    forceWrite = false;
588
                    writeStyle = attr.Attribute.Name == "fill" || attr.Attribute.Name == "stroke";
589

590
                    if (writeStyle && (Parent != null))
591
                    {
592
                    	if(propertyValue == SvgColourServer.NotSet) continue;
593
                    	
594
595
                        object parentValue;
                        if (TryResolveParentAttributeValue(attr.Attribute.Name, out parentValue))
596
                        {
597
598
                            if ((parentValue == propertyValue) 
                                || ((parentValue != null) &&  parentValue.Equals(propertyValue)))
599
                                continue;
600
                            
601
602
603
604
                            forceWrite = true;
                        }
                    }

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

636
            //events
Tebjan Halm's avatar
Tebjan Halm committed
637
            if(AutoPublishEvents)
638
            {
639
640
641
	            foreach (var attr in _svgEventAttributes)
	            {
	                var evt = attr.Event.GetValue(this);
Eric Domke's avatar
Eric Domke committed
642

643
	                //if someone has registered publish the attribute
Tebjan Halm's avatar
Tebjan Halm committed
644
	                if (evt != null && !string.IsNullOrEmpty(this.ID))
645
646
647
648
	                {
	                    writer.WriteAttributeString(attr.Attribute.Name, this.ID + "/" + attr.Attribute.Name);
	                }
	            }
649
650
            }

651
652
653
654
655
            //add the custom attributes
            foreach (var item in this._customAttributes)
            {
                writer.WriteAttributeString(item.Key, item.Value);
            }
Eric Domke's avatar
Eric Domke committed
656
657
658
659
660
661
662

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

667
        private bool TryResolveParentAttributeValue(string attributeKey, out object parentAttributeValue)
668
        {
669
            parentAttributeValue = null;
670

tebjan's avatar
tebjan committed
671
            //attributeKey = char.ToUpper(attributeKey[0]) + attributeKey.Substring(1);
672
673

            var currentParent = Parent;
674
            var resolved = false;
675
676
677
678
            while (currentParent != null)
            {
                if (currentParent.Attributes.ContainsKey(attributeKey))
                {
679
680
681
                    resolved = true;
                    parentAttributeValue = currentParent.Attributes[attributeKey];
                    if (parentAttributeValue != null)
682
683
684
685
                        break;
                }
                currentParent = currentParent.Parent;
            }
686

687
            return resolved;
davescriven's avatar
davescriven committed
688
689
        }

690
        public virtual void Write(XmlTextWriter writer)
davescriven's avatar
davescriven committed
691
        {
692
            if (ShouldWriteElement())
davescriven's avatar
davescriven committed
693
694
695
696
697
698
699
700
701
            {
                this.WriteStartElement(writer);
                this.WriteChildren(writer);
                this.WriteEndElement(writer);
            }
        }

        protected virtual void WriteChildren(XmlTextWriter writer)
        {
Eric Domke's avatar
Eric Domke committed
702
            if (this.Nodes.Any())
davescriven's avatar
davescriven committed
703
            {
Eric Domke's avatar
Eric Domke committed
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
                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
729
730
731
732
            }
        }

        /// <summary>
Eric Domke's avatar
Eric Domke committed
733
        /// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="ISvgRenderer"/> object.
davescriven's avatar
davescriven committed
734
        /// </summary>
Eric Domke's avatar
Eric Domke committed
735
736
        /// <param name="renderer">The <see cref="ISvgRenderer"/> object to render to.</param>
        protected virtual void Render(ISvgRenderer renderer)
davescriven's avatar
davescriven committed
737
        {
738
739
740
            this.PushTransforms(renderer);
            this.RenderChildren(renderer);
            this.PopTransforms(renderer);
davescriven's avatar
davescriven committed
741
742
        }

743
744
745
        /// <summary>
        /// Renders the children of this <see cref="SvgElement"/>.
        /// </summary>
Eric Domke's avatar
Eric Domke committed
746
747
        /// <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
748
749
750
        {
            foreach (SvgElement element in this.Children)
            {
751
                element.Render(renderer);
davescriven's avatar
davescriven committed
752
753
754
            }
        }

755
        /// <summary>
Eric Domke's avatar
Eric Domke committed
756
        /// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="ISvgRenderer"/> object.
757
        /// </summary>
Eric Domke's avatar
Eric Domke committed
758
759
        /// <param name="renderer">The <see cref="ISvgRenderer"/> object to render to.</param>
        void ISvgElement.Render(ISvgRenderer renderer)
davescriven's avatar
davescriven committed
760
        {
761
            this.Render(renderer);
davescriven's avatar
davescriven committed
762
        }
Tebjan Halm's avatar
Tebjan Halm committed
763
764
765
766
767
768
769
770
        
        /// <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
771
        	foreach(var child in elem.Children)
Tebjan Halm's avatar
Tebjan Halm committed
772
        	{
773
774
775
776
777
778
            // 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
779
        		if (child is SvgVisualElement)
Tebjan Halm's avatar
Tebjan Halm committed
780
        		{
Tebjan Halm's avatar
Tebjan Halm committed
781
        			if(!(child is SvgGroup))
782
        			{
783
        				var childPath = ((SvgVisualElement)child).Path(null);
784
        				
Tebjan Halm's avatar
Tebjan Halm committed
785
786
787
788
789
        				if (childPath != null)
        				{
        					childPath = (GraphicsPath)childPath.Clone();
        					if(child.Transforms != null)
        						childPath.Transform(child.Transforms.GetMatrix());
790
791

                            if (childPath.PointCount > 0) path.AddPath(childPath, false);
Tebjan Halm's avatar
Tebjan Halm committed
792
        				}
793
        			}
Tebjan Halm's avatar
Tebjan Halm committed
794
        		}
795
796

                if (!(child is SvgPaintServer)) AddPaths(child, path);
Tebjan Halm's avatar
Tebjan Halm committed
797
        	}
Tebjan Halm's avatar
Tebjan Halm committed
798
799
800
801
802
803
        }
        
        /// <summary>
        /// Recursive method to add up the paths of all children
        /// </summary>
        /// <param name="elem"></param>
804
        /// <param name="renderer"></param>
Eric Domke's avatar
Eric Domke committed
805
        protected GraphicsPath GetPaths(SvgElement elem, ISvgRenderer renderer)
Tebjan Halm's avatar
Tebjan Halm committed
806
807
808
809
810
811
812
813
814
        {
        	var ret = new GraphicsPath();
        	
        	foreach(var child in elem.Children)
        	{
        		if (child is SvgVisualElement)
        		{
        			if(!(child is SvgGroup))
        			{
815
                        var childPath = ((SvgVisualElement)child).Path(renderer);
Tebjan Halm's avatar
Tebjan Halm committed
816
        				
817
818
819
820
      					// 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);

821
        				if (childPath != null && childPath.PointCount > 0)
Tebjan Halm's avatar
Tebjan Halm committed
822
823
824
825
826
827
828
829
830
831
        				{
        					childPath = (GraphicsPath)childPath.Clone();
        					if(child.Transforms != null)
        						childPath.Transform(child.Transforms.GetMatrix());
        					
        					ret.AddPath(childPath, false);
        				}
        			}
        			else
        			{
832
833
834
835
836
837
838
839
				        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
840
841
842
843
        			}
        		}
        			
        	}
Tebjan Halm's avatar
Tebjan Halm committed
844
        	
Tebjan Halm's avatar
Tebjan Halm committed
845
        	return ret;
Tebjan Halm's avatar
Tebjan Halm committed
846
        }
davescriven's avatar
davescriven committed
847

848
849
850
851
852
853
        /// <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
854
855
856
857
        public virtual object Clone()
        {
            return this.MemberwiseClone();
        }
858
859
860

    	public abstract SvgElement DeepCopy();

861
862
863
864
865
        ISvgNode ISvgNode.DeepCopy()
        {
            return DeepCopy();
        }

866
867
868
		public virtual SvgElement DeepCopy<T>() where T : SvgElement, new()
		{
			var newObj = new T();
869
			newObj.ID = this.ID;
870
871
			newObj.Content = this.Content;
			newObj.ElementName = this.ElementName;
Eric Domke's avatar
Eric Domke committed
872

873
874
875
876
877
//			if (this.Parent != null)
	//			this.Parent.Children.Add(newObj);

			if (this.Transforms != null)
			{
878
				newObj.Transforms = this.Transforms.Clone() as SvgTransformCollection;
879
880
881
882
883
884
			}

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

886
887
888
			foreach (var attr in this._svgEventAttributes)
			{
				var evt = attr.Event.GetValue(this);
Eric Domke's avatar
Eric Domke committed
889

890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
				//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
905
906
					else if (attr.Event.Name == "Click")
						newObj.Click += delegate {  };
907
908
					else if (attr.Event.Name == "Change") //text element
						(newObj as SvgText).Change += delegate {  };
909
910
				}
			}
Eric Domke's avatar
Eric Domke committed
911

912
913
914
915
916
917
918
			if(this._customAttributes.Count > 0)
			{
				foreach (var element in _customAttributes) 
				{
					newObj.CustomAttributes.Add(element.Key, element.Value);
				}
			}
Eric Domke's avatar
Eric Domke committed
919

920
921
922
923
924
925
926
927
            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
928
        }
Eric Domke's avatar
Eric Domke committed
929

930
931
932
933
		/// <summary>
        /// Fired when an Atrribute of this Element has changed
        /// </summary>
		public event EventHandler<AttributeEventArgs> AttributeChanged;
Eric Domke's avatar
Eric Domke committed
934

935
936
937
938
939
940
941
942
		protected void OnAttributeChanged(AttributeEventArgs args)
		{
			var handler = AttributeChanged;
			if(handler != null)
			{
				handler(this, args);
			}
		}
Eric Domke's avatar
Eric Domke committed
943

tebjan's avatar
tebjan committed
944
945
946
947
		/// <summary>
        /// Fired when an Atrribute of this Element has changed
        /// </summary>
		public event EventHandler<ContentEventArgs> ContentChanged;
Eric Domke's avatar
Eric Domke committed
948

tebjan's avatar
tebjan committed
949
950
951
952
953
954
955
956
		protected void OnContentChanged(ContentEventArgs args)
		{
			var handler = ContentChanged;
			if(handler != null)
			{
				handler(this, args);
			}
		}
Tebjan Halm's avatar
Tebjan Halm committed
957
958
959
960

        #region graphical EVENTS

        /*  
961
962
963
            	onfocusin = "<anything>"
            	onfocusout = "<anything>"
            	onactivate = "<anything>"
964
965
966
967
968
                onclick = "<anything>"
                onmousedown = "<anything>"
                onmouseup = "<anything>"
                onmouseover = "<anything>"
                onmousemove = "<anything>"
969
            	onmouseout = "<anything>" 
Tebjan Halm's avatar
Tebjan Halm committed
970
971
         */

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

985
986
987
988
989
990
                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));
991
                caller.RegisterAction<int, bool, bool, bool, string>(rpcID + "onmousescroll", OnMouseScroll);
992
993
            }
        }
994
995
996
997
998
        
        /// <summary>
        /// Use this method to provide your implementation ISvgEventCaller to unregister Actions
        /// </summary>
        /// <param name="caller"></param>
999
        public virtual void UnregisterEvents(ISvgEventCaller caller)
1000
        {