SvgFragment.cs 9.75 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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
        /// <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")]
        public virtual SvgUnit FontSize
        {
            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")]
        public virtual string FontFamily
        {
            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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188

        protected override void Render(ISvgRenderer renderer)
        {
            switch (this.Overflow)
            {
                case SvgOverflow.auto:
                case SvgOverflow.visible:
                case SvgOverflow.scroll:
                    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),
                                                  this.Y.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this),
                                                  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
            {
Tebjan Halm's avatar
Tebjan Halm committed
214
                return this.Path.GetBounds();
James Welle's avatar
James Welle committed
215
            }
216
        }
davescriven's avatar
davescriven committed
217
218
219
220
221
222

        /// <summary>
        /// Initializes a new instance of the <see cref="SvgFragment"/> class.
        /// </summary>
        public SvgFragment()
        {
James Welle's avatar
James Welle committed
223
            _x = 0.0f;
tebjan's avatar
tebjan committed
224
            _y = 0.0f;
225
226
            this.Height = new SvgUnit(SvgUnitType.Percentage, 100.0f);
            this.Width = new SvgUnit(SvgUnitType.Percentage, 100.0f);
davescriven's avatar
davescriven committed
227
            this.ViewBox = SvgViewBox.Empty;
Brian C. Barnes's avatar
Brian C. Barnes committed
228
            this.AspectRatio = new SvgAspectRatio(SvgPreserveAspectRatio.xMidYMid);
davescriven's avatar
davescriven committed
229
        }
230

James Welle's avatar
James Welle committed
231
232
        public SizeF GetDimensions()
        {
233
            float w, h;
James Welle's avatar
James Welle committed
234
235
236
            var isWidthperc = Width.Type == SvgUnitType.Percentage;
            var isHeightperc = Height.Type == SvgUnitType.Percentage;

237
            RectangleF bounds = new RectangleF();
James Welle's avatar
James Welle committed
238
239
            if (isWidthperc || isHeightperc)
            {
240
241
242
243
244
245
                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
246
                    bounds = this.Bounds; //do just one call to the recursive bounds property
247
248
249
                }
            }

250
            if (isWidthperc)
251
252
253
254
255
256
257
            {
                w = (bounds.Width + bounds.X) * (Width.Value * 0.01f);
            }
            else
            {
                w = Width.ToDeviceValue(null, UnitRenderingType.Horizontal, this);
            }
258
            if (isHeightperc)
259
260
261
            {
                h = (bounds.Height + bounds.Y) * (Height.Value * 0.01f);
            }
262
            else
263
264
            {
                h = Height.ToDeviceValue(null, UnitRenderingType.Vertical, this);
James Welle's avatar
James Welle committed
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
            }

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

286
287
288
289
        //Override the default behavior, writing out the namespaces.
        protected override void WriteStartElement(XmlTextWriter writer)
        {
            base.WriteStartElement(writer);
290

291
292
293
294
295
296
297
298
299
300
            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
301
302
    }
}