SvgFragment.cs 10.8 KB
Newer Older
davescriven's avatar
davescriven committed
1
2
using System;
using System.Drawing;
James Welle's avatar
James Welle committed
3
using System.Drawing.Drawing2D;
4
using System.Xml;
davescriven's avatar
davescriven committed
5
6
7
8
9
10

namespace Svg
{
    /// <summary>
    /// An <see cref="SvgFragment"/> represents an SVG fragment that can be the root element or an embedded fragment of an SVG document.
    /// </summary>
11
    [SvgElement("svg")]
James Welle's avatar
James Welle committed
12
    public class SvgFragment : SvgElement, ISvgViewPort, ISvgBoundable
davescriven's avatar
davescriven committed
13
14
15
16
17
    {
        /// <summary>
        /// Gets the SVG namespace string.
        /// </summary>
        public static readonly Uri Namespace = new Uri("http://www.w3.org/2000/svg");
James Welle's avatar
James Welle committed
18

Tebjan Halm's avatar
Tebjan Halm committed
19
        PointF ISvgBoundable.Location
James Welle's avatar
James Welle committed
20
        {
Tebjan Halm's avatar
Tebjan Halm committed
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
            get
            {
                return PointF.Empty;
            }
        }

        SizeF ISvgBoundable.Size
        {
            get
            {
                return GetDimensions();
            }
        }

        RectangleF ISvgBoundable.Bounds
        {
            get
            {
                return new RectangleF(((ISvgBoundable)this).Location, ((ISvgBoundable)this).Size);
            }
James Welle's avatar
James Welle committed
41
42
        }

tebjan's avatar
tebjan committed
43
44
        private SvgUnit _x;
        private SvgUnit _y;
45

tebjan's avatar
tebjan committed
46
47
48
49
50
51
        /// <summary>
        /// Gets or sets the position where the left point of the svg should start.
        /// </summary>
        [SvgAttribute("x")]
        public SvgUnit X
        {
James Welle's avatar
James Welle committed
52
53
54
            get { return _x; }
            set
            {
55
                if (_x != value)
James Welle's avatar
James Welle committed
56
57
                {
                    _x = value;
58
                    OnAttributeChanged(new AttributeEventArgs { Attribute = "x", Value = value });
James Welle's avatar
James Welle committed
59
60
                }
            }
tebjan's avatar
tebjan committed
61
62
63
64
65
66
67
68
        }

        /// <summary>
        /// Gets or sets the position where the top point of the svg should start.
        /// </summary>
        [SvgAttribute("y")]
        public SvgUnit Y
        {
James Welle's avatar
James Welle committed
69
70
71
            get { return _y; }
            set
            {
72
                if (_y != value)
James Welle's avatar
James Welle committed
73
74
                {
                    _y = value;
75
                    OnAttributeChanged(new AttributeEventArgs { Attribute = "y", Value = value });
James Welle's avatar
James Welle committed
76
77
                }
            }
tebjan's avatar
tebjan committed
78
        }
davescriven's avatar
davescriven committed
79
80

        /// <summary>
81
        /// Gets or sets the width of the fragment.
davescriven's avatar
davescriven committed
82
83
84
85
86
        /// </summary>
        /// <value>The width.</value>
        [SvgAttribute("width")]
        public SvgUnit Width
        {
87
            get { return this.Attributes.GetAttribute<SvgUnit>("width"); }
James Welle's avatar
James Welle committed
88
            set { this.Attributes["width"] = value; }
davescriven's avatar
davescriven committed
89
90
91
        }

        /// <summary>
92
        /// Gets or sets the height of the fragment.
davescriven's avatar
davescriven committed
93
94
95
96
97
        /// </summary>
        /// <value>The height.</value>
        [SvgAttribute("height")]
        public SvgUnit Height
        {
98
            get { return this.Attributes.GetAttribute<SvgUnit>("height"); }
James Welle's avatar
James Welle committed
99
            set { this.Attributes["height"] = value; }
davescriven's avatar
davescriven committed
100
101
        }

James Welle's avatar
James Welle committed
102
103
104
105
106
107
        [SvgAttribute("overflow")]
        public virtual SvgOverflow Overflow
        {
            get { return this.Attributes.GetAttribute<SvgOverflow>("overflow"); }
            set { this.Attributes["overflow"] = value; }
        }
108

davescriven's avatar
davescriven committed
109
110
111
112
113
114
115
        /// <summary>
        /// Gets or sets the viewport of the element.
        /// </summary>
        /// <value></value>
        [SvgAttribute("viewBox")]
        public SvgViewBox ViewBox
        {
116
117
            get { return this.Attributes.GetAttribute<SvgViewBox>("viewBox"); }
            set { this.Attributes["viewBox"] = value; }
davescriven's avatar
davescriven committed
118
        }
119

120
121
122
123
        /// <summary>
        /// Gets or sets the aspect of the viewport.
        /// </summary>
        /// <value></value>
James Welle's avatar
James Welle committed
124
        [SvgAttribute("preserveAspectRatio")]
125
126
        public SvgAspectRatio AspectRatio
        {
James Welle's avatar
James Welle committed
127
128
            get { return this.Attributes.GetAttribute<SvgAspectRatio>("preserveAspectRatio"); }
            set { this.Attributes["preserveAspectRatio"] = value; }
129
        }
davescriven's avatar
davescriven committed
130

131
132
133
134
        /// <summary>
        /// Refers to the size of the font from baseline to baseline when multiple lines of text are set solid in a multiline layout environment.
        /// </summary>
        [SvgAttribute("font-size")]
135
        public override SvgUnit FontSize
136
137
138
139
140
141
142
143
144
        {
            get { return (this.Attributes["font-size"] == null) ? SvgUnit.Empty : (SvgUnit)this.Attributes["font-size"]; }
            set { this.Attributes["font-size"] = value; }
        }

        /// <summary>
        /// Indicates which font family is to be used to render the text.
        /// </summary>
        [SvgAttribute("font-family")]
145
        public override string FontFamily
146
147
148
149
150
        {
            get { return this.Attributes["font-family"] as string; }
            set { this.Attributes["font-family"] = value; }
        }

davescriven's avatar
davescriven committed
151
        /// <summary>
Eric Domke's avatar
Eric Domke committed
152
        /// Applies the required transforms to <see cref="ISvgRenderer"/>.
davescriven's avatar
davescriven committed
153
        /// </summary>
Eric Domke's avatar
Eric Domke committed
154
155
        /// <param name="renderer">The <see cref="ISvgRenderer"/> to be transformed.</param>
        protected internal override bool PushTransforms(ISvgRenderer renderer)
davescriven's avatar
davescriven committed
156
        {
157
            if (!base.PushTransforms(renderer)) return false;
Eric Domke's avatar
Eric Domke committed
158
            this.ViewBox.AddViewBoxTransform(this.AspectRatio, renderer, this);
159
            return true;
davescriven's avatar
davescriven committed
160
        }
Eric Domke's avatar
Eric Domke committed
161
162
163
164
165

        protected override void Render(ISvgRenderer renderer)
        {
            switch (this.Overflow)
            {
166
167
                case SvgOverflow.Auto:
                case SvgOverflow.Visible:
168
                case SvgOverflow.Inherit:
Eric Domke's avatar
Eric Domke committed
169
170
171
172
173
174
175
176
                    base.Render(renderer);
                    break;
                default:
                    var prevClip = renderer.GetClip();
                    try
                    {
                        var size = (this.Parent == null ? renderer.GetBoundable().Bounds.Size : GetDimensions());
                        var clip = new RectangleF(this.X.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this),
177
                                                  this.Y.ToDeviceValue(renderer, UnitRenderingType.Vertical, this),
Eric Domke's avatar
Eric Domke committed
178
179
180
181
182
183
184
185
186
187
188
                                                  size.Width, size.Height);
                        renderer.SetClip(new Region(clip), CombineMode.Intersect);
                        base.Render(renderer);
                    }
                    finally
                    {
                        renderer.SetClip(prevClip, CombineMode.Replace);
                    }
                    break;
            }
        }
189

Tebjan Halm's avatar
Tebjan Halm committed
190
191
192
193
        /// <summary>
        /// Gets the <see cref="GraphicsPath"/> for this element.
        /// </summary>
        /// <value></value>
Tebjan Halm's avatar
Tebjan Halm committed
194
        public GraphicsPath Path
Tebjan Halm's avatar
Tebjan Halm committed
195
        {
196
197
            get
            {
Tebjan Halm's avatar
Tebjan Halm committed
198
                var path = new GraphicsPath();
Dan Backes's avatar
Dan Backes committed
199

Tebjan Halm's avatar
Tebjan Halm committed
200
                AddPaths(this, path);
201

Tebjan Halm's avatar
Tebjan Halm committed
202
203
                return path;
            }
Tebjan Halm's avatar
Tebjan Halm committed
204
        }
205

206
207
208
        /// <summary>
        /// Gets the bounds of the svg element.
        /// </summary>
Tebjan Halm's avatar
Tebjan Halm committed
209
        /// <value>The bounds.</value>
210
211
        public RectangleF Bounds
        {
Tebjan Halm's avatar
Tebjan Halm committed
212
            get
James Welle's avatar
James Welle committed
213
            {
214
215
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
                var bounds = new RectangleF();
                foreach (var child in this.Children)
                {
                    RectangleF childBounds = new RectangleF();
                    if (child is SvgFragment)
                    {
                        childBounds = ((SvgFragment)child).Bounds;
                        childBounds.Offset(((SvgFragment)child).X, ((SvgFragment)child).Y);
                    }
                    else if (child is SvgVisualElement)
                    {
                        childBounds = ((SvgVisualElement)child).Bounds;
                    }

                    if (!childBounds.IsEmpty)
                    {
                        if (bounds.IsEmpty)
                        {
                            bounds = childBounds;
                        }
                        else
                        {
                            bounds = RectangleF.Union(bounds, childBounds);
                        }
                    }
                }

                return bounds;
James Welle's avatar
James Welle committed
242
            }
243
        }
davescriven's avatar
davescriven committed
244
245
246
247
248
249

        /// <summary>
        /// Initializes a new instance of the <see cref="SvgFragment"/> class.
        /// </summary>
        public SvgFragment()
        {
James Welle's avatar
James Welle committed
250
            _x = 0.0f;
tebjan's avatar
tebjan committed
251
            _y = 0.0f;
252
253
            this.Height = new SvgUnit(SvgUnitType.Percentage, 100.0f);
            this.Width = new SvgUnit(SvgUnitType.Percentage, 100.0f);
davescriven's avatar
davescriven committed
254
            this.ViewBox = SvgViewBox.Empty;
Brian C. Barnes's avatar
Brian C. Barnes committed
255
            this.AspectRatio = new SvgAspectRatio(SvgPreserveAspectRatio.xMidYMid);
davescriven's avatar
davescriven committed
256
        }
257

James Welle's avatar
James Welle committed
258
259
        public SizeF GetDimensions()
        {
260
            float w, h;
James Welle's avatar
James Welle committed
261
262
263
            var isWidthperc = Width.Type == SvgUnitType.Percentage;
            var isHeightperc = Height.Type == SvgUnitType.Percentage;

264
            RectangleF bounds = new RectangleF();
James Welle's avatar
James Welle committed
265
266
            if (isWidthperc || isHeightperc)
            {
267
268
269
270
271
272
                if (ViewBox.Width > 0 && ViewBox.Height > 0)
                {
                    bounds = new RectangleF(ViewBox.MinX, ViewBox.MinY, ViewBox.Width, ViewBox.Height);
                }
                else
                {
Tebjan Halm's avatar
Tebjan Halm committed
273
                    bounds = this.Bounds; //do just one call to the recursive bounds property
274
275
276
                }
            }

277
            if (isWidthperc)
278
            {
279
                w = (bounds.Width + bounds.X) * (Width.Value * 0.01f);
280
281
282
283
284
            }
            else
            {
                w = Width.ToDeviceValue(null, UnitRenderingType.Horizontal, this);
            }
285
            if (isHeightperc)
286
            {
287
                h = (bounds.Height + bounds.Y) * (Height.Value * 0.01f);
288
            }
289
            else
290
291
            {
                h = Height.ToDeviceValue(null, UnitRenderingType.Vertical, this);
James Welle's avatar
James Welle committed
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
            }

            return new SizeF(w, h);
        }

        public override SvgElement DeepCopy()
        {
            return DeepCopy<SvgFragment>();
        }

        public override SvgElement DeepCopy<T>()
        {
            var newObj = base.DeepCopy<T>() as SvgFragment;
            newObj.Height = this.Height;
            newObj.Width = this.Width;
            newObj.Overflow = this.Overflow;
            newObj.ViewBox = this.ViewBox;
            newObj.AspectRatio = this.AspectRatio;
            return newObj;
        }
312

313
314
315
316
        //Override the default behavior, writing out the namespaces.
        protected override void WriteStartElement(XmlTextWriter writer)
        {
            base.WriteStartElement(writer);
317

318
319
320
321
322
323
324
325
326
327
            foreach (var ns in SvgAttributeAttribute.Namespaces)
            {
                if (string.IsNullOrEmpty(ns.Key))
                    writer.WriteAttributeString("xmlns", ns.Value);
                else
                    writer.WriteAttributeString("xmlns:" + ns.Key, ns.Value);
            }

            writer.WriteAttributeString("version", "1.1");
        }
davescriven's avatar
davescriven committed
328
329
    }
}