SvgDocument.cs 12.4 KB
Newer Older
davescriven's avatar
davescriven committed
1
2
3
4
5
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
6
7
using System.Drawing.Drawing2D;
using System.Drawing.Text;
davescriven's avatar
davescriven committed
8
9
10
11
12
13
14
15
16
17
18
19
using System.IO;
using System.Text;
using System.Xml;

namespace Svg
{
    /// <summary>
    /// The class used to create and load all SVG documents.
    /// </summary>
    public class SvgDocument : SvgFragment, ITypeDescriptorContext
    {
        public static readonly int PPI = 96;
20

davescriven's avatar
davescriven committed
21
22
23
24
25
        /// <summary>
        /// Gets a <see cref="string"/> containing the XLink namespace (http://www.w3.org/1999/xlink).
        /// </summary>
        public static readonly string XLinkNamespace = "http://www.w3.org/1999/xlink";

26
27
        private SvgElementIdManager _idManager;

28
29
30
        /// <summary>
        /// Initializes a new instance of the <see cref="SvgDocument"/> class.
        /// </summary>
31
        public SvgDocument()
davescriven's avatar
davescriven committed
32
        {
33
            Ppi = 96;
davescriven's avatar
davescriven committed
34
35
36
37
38
39
40
41
42
        }

        /// <summary>
        /// Gets an <see cref="SvgElementIdManager"/> for this document.
        /// </summary>
        protected internal virtual SvgElementIdManager IdManager
        {
            get
            {
43
                if (_idManager == null)
44
                {
45
                    _idManager = new SvgElementIdManager(this);
46
                }
davescriven's avatar
davescriven committed
47

48
                return _idManager;
davescriven's avatar
davescriven committed
49
50
51
            }
        }

52
53
54
55
56
        public int Ppi { get; set; }

        #region ITypeDescriptorContext Members

        IContainer ITypeDescriptorContext.Container
davescriven's avatar
davescriven committed
57
        {
58
            get { throw new NotImplementedException(); }
davescriven's avatar
davescriven committed
59
60
        }

61
        object ITypeDescriptorContext.Instance
davescriven's avatar
davescriven committed
62
        {
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
            get { return this; }
        }

        void ITypeDescriptorContext.OnComponentChanged()
        {
            throw new NotImplementedException();
        }

        bool ITypeDescriptorContext.OnComponentChanging()
        {
            throw new NotImplementedException();
        }

        PropertyDescriptor ITypeDescriptorContext.PropertyDescriptor
        {
            get { throw new NotImplementedException(); }
        }

        object IServiceProvider.GetService(Type serviceType)
        {
            throw new NotImplementedException();
        }

        #endregion

        /// <summary>
        /// Retrieves the <see cref="SvgElement"/> with the specified ID.
        /// </summary>
        /// <param name="id">A <see cref="string"/> containing the ID of the element to find.</param>
        /// <returns>An <see cref="SvgElement"/> of one exists with the specified ID; otherwise false.</returns>
        public virtual SvgElement GetElementById(string id)
        {
            return IdManager.GetElementById(id);
davescriven's avatar
davescriven committed
96
97
        }

98
99
100
101
102
103
104
105
106
107
        /// <summary>
        /// Retrieves the <see cref="SvgElement"/> with the specified ID.
        /// </summary>
        /// <param name="id">A <see cref="string"/> containing the ID of the element to find.</param>
        /// <returns>An <see cref="SvgElement"/> of one exists with the specified ID; otherwise false.</returns>
        public virtual TSvgElement GetElementById<TSvgElement>(string id) where TSvgElement : SvgElement
        {
            return (this.GetElementById(id) as TSvgElement);
        }

davescriven's avatar
davescriven committed
108
109
110
111
112
113
114
        /// <summary>
        /// Opens the document at the specified path and loads the contents.
        /// </summary>
        /// <param name="path">A <see cref="string"/> containing the path of the file to open.</param>
        /// <returns>An <see cref="SvgDocument"/> with the contents loaded.</returns>
        /// <exception cref="FileNotFoundException">The document at the specified <paramref name="path"/> cannot be found.</exception>
        public static SvgDocument Open(string path)
115
116
117
118
        {
            return Open(path, null);
        }

119
120
121
122
123
124
        /// <summary>
        /// Opens the document at the specified path and loads the contents.
        /// </summary>
        /// <param name="path">A <see cref="string"/> containing the path of the file to open.</param>
        /// <param name="entities">A dictionary of custom entity definitions to be used when resolving XML entities within the document.</param>
        /// <returns>An <see cref="SvgDocument"/> with the contents loaded.</returns>
125
        public static SvgDocument Open(string path, Dictionary<string, string> entities)
davescriven's avatar
davescriven committed
126
        {
127
128
129
130
131
            if (string.IsNullOrEmpty(path))
            {
                throw new ArgumentNullException("path");
            }

davescriven's avatar
davescriven committed
132
            if (!File.Exists(path))
133
            {
davescriven's avatar
davescriven committed
134
                throw new FileNotFoundException("The specified document cannot be found.", path);
135
            }
davescriven's avatar
davescriven committed
136

137
            return Open(File.OpenRead(path), entities);
davescriven's avatar
davescriven committed
138
139
        }

140
141
142
143
        /// <summary>
        /// Attempts to open an SVG document from the specified <see cref="Stream"/>.
        /// </summary>
        /// <param name="stream">The <see cref="Stream"/> containing the SVG document to open.</param>
davescriven's avatar
davescriven committed
144
145
146
147
148
        public static SvgDocument Open(Stream stream)
        {
            return Open(stream, null);
        }

149
150
151
152
153
        /// <summary>
        /// Attempts to open an SVG document from the specified <see cref="Stream"/> and adds the specified entities.
        /// </summary>
        /// <param name="stream">The <see cref="Stream"/> containing the SVG document to open.</param>
        /// <param name="entities">Custom entity definitions.</param>
154
        public static SvgDocument Open(Stream stream, Dictionary<string, string> entities)
davescriven's avatar
davescriven committed
155
        {
156
157
158
159
160
            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }

davescriven's avatar
davescriven committed
161
162
            Trace.TraceInformation("Begin Read");

163
            using (var reader = new SvgTextReader(stream, entities))
davescriven's avatar
davescriven committed
164
            {
165
166
                var elementStack = new Stack<SvgElement>();
                var value = new StringBuilder();
davescriven's avatar
davescriven committed
167
                SvgElement element = null;
168
                SvgElement parent;
davescriven's avatar
davescriven committed
169
170
171
172
173
174
175
176
177
178
179
180
181
                SvgDocument svgDocument = null;
                reader.XmlResolver = new SvgDtdResolver();
                reader.WhitespaceHandling = WhitespaceHandling.None;

                while (reader.Read())
                {
                    try
                    {
                        switch (reader.NodeType)
                        {
                            case XmlNodeType.Element:
                                // Does this element have a value or children
                                // (Must do this check here before we progress to another node)
182
                                bool isEmpty = reader.IsEmptyElement;
davescriven's avatar
davescriven committed
183
184
                                // Create element
                                if (elementStack.Count > 0)
185
                                {
davescriven's avatar
davescriven committed
186
                                    element = SvgElementFactory.CreateElement(reader, svgDocument);
187
                                }
davescriven's avatar
davescriven committed
188
189
190
                                else
                                {
                                    element = SvgElementFactory.CreateDocument(reader);
191
                                    svgDocument = (SvgDocument)element;
davescriven's avatar
davescriven committed
192
193
194
                                }

                                if (element == null)
195
                                {
davescriven's avatar
davescriven committed
196
                                    continue;
197
                                }
davescriven's avatar
davescriven committed
198
199
200
201
202
203
204
205
206
207
208
209
210

                                // Add to the parents children
                                if (elementStack.Count > 0)
                                {
                                    parent = elementStack.Peek();
                                    parent.Children.Add(element);
                                }

                                // Push element into stack
                                elementStack.Push(element);

                                // Need to process if the element is empty
                                if (isEmpty)
211
                                {
davescriven's avatar
davescriven committed
212
                                    goto case XmlNodeType.EndElement;
213
                                }
davescriven's avatar
davescriven committed
214
215
216

                                break;
                            case XmlNodeType.EndElement:
217
218
219
220
                                // Skip if no element was created and is not the closing tag for the last
                                // known element
                                if (element == null && reader.LocalName != elementStack.Peek().ElementName)
                                {
davescriven's avatar
davescriven committed
221
                                    continue;
222
                                }
davescriven's avatar
davescriven committed
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
249
                                // Pop the element out of the stack
                                element = elementStack.Pop();

                                if (value.Length > 0)
                                {
                                    element.Content = value.ToString();
                                    // Reset content value for new element
                                    value = new StringBuilder();
                                }
                                break;
                            case XmlNodeType.CDATA:
                            case XmlNodeType.Text:
                                value.Append(reader.Value);
                                break;
                        }
                    }
                    catch (Exception exc)
                    {
                        Trace.TraceError(exc.Message);
                    }
                }

                Trace.TraceInformation("End Read");
                return svgDocument;
            }
        }

250
        public static SvgDocument Open(XmlDocument document)
davescriven's avatar
davescriven committed
251
252
253
254
255
256
257
258
259
        {
            return null;
        }

        public static Bitmap OpenAsBitmap(string path)
        {
            return null;
        }

260
        public static Bitmap OpenAsBitmap(XmlDocument document)
davescriven's avatar
davescriven committed
261
262
263
264
265
266
        {
            return null;
        }

        public RectangleF GetDimensions()
        {
267
            return new RectangleF(0, 0, Width.ToDeviceValue(), Height.ToDeviceValue());
davescriven's avatar
davescriven committed
268
269
        }

270
271
272
273
        /// <summary>
        /// Renders the <see cref="SvgDocument"/> to the specified <see cref="SvgRenderer"/>.
        /// </summary>
        /// <param name="renderer">The <see cref="SvgRenderer"/> to render the document with.</param>
274
        /// <exception cref="ArgumentNullException">The <paramref name="renderer"/> parameter cannot be <c>null</c>.</exception>
275
        public void Draw(SvgRenderer renderer)
davescriven's avatar
davescriven committed
276
        {
277
278
279
280
281
            if (renderer == null)
            {
                throw new ArgumentNullException("renderer");
            }

282
            Render(renderer);
davescriven's avatar
davescriven committed
283
284
        }

285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
        /// <summary>
        /// Renders the <see cref="SvgDocument"/> to the specified <see cref="Graphics"/>.
        /// </summary>
        /// <param name="graphics">The <see cref="Graphics"/> to be rendered to.</param>
        /// <exception cref="ArgumentNullException">The <paramref name="graphics"/> parameter cannot be <c>null</c>.</exception>
        public void Draw(Graphics graphics)
        {
            if (graphics == null)
            {
                throw new ArgumentNullException("graphics");
            }

            Render(SvgRenderer.FromGraphics(graphics));
        }

300
301
302
303
        /// <summary>
        /// Renders the <see cref="SvgDocument"/> and returns the image as a <see cref="Bitmap"/>.
        /// </summary>
        /// <returns>A <see cref="Bitmap"/> containing the rendered document.</returns>
davescriven's avatar
davescriven committed
304
305
306
307
        public virtual Bitmap Draw()
        {
            Trace.TraceInformation("Begin Render");

308
            var size = GetDimensions();
309
            var bitmap = new Bitmap((int)Math.Ceiling(size.Width), (int)Math.Ceiling(size.Height));
davescriven's avatar
davescriven committed
310

311
            try
davescriven's avatar
davescriven committed
312
            {
313
                using (var renderer = SvgRenderer.FromImage(bitmap))
314
                {
315
316
317
318
319
                    renderer.TextRenderingHint = TextRenderingHint.AntiAlias;
                    renderer.TextContrast = 1;
                    renderer.PixelOffsetMode = PixelOffsetMode.Half;
                    this.Render(renderer);
                    renderer.Save();
320
321
322
323
324
325
                }
            }
            catch
            {
                bitmap.Dispose();
                throw;
davescriven's avatar
davescriven committed
326
327
328
329
330
331
332
333
            }

            Trace.TraceInformation("End Render");
            return bitmap;
        }

        public void Write(Stream stream)
        {
334

davescriven's avatar
davescriven committed
335
336
337
338
        }

        public void Write(string path)
        {
339

davescriven's avatar
davescriven committed
340
341
342
        }
    }
}