Click here to Skip to main content
15,895,011 members
Articles / Desktop Programming / WPF

SharpVectors - SVG# Reloaded: An Introduction

Rate me:
Please Sign up or sign in to vote.
4.98/5 (33 votes)
17 Nov 2010BSD10 min read 205.8K   21.7K   101  
A C# library for converting SVG to WPF and viewing SVG files in WPF Applications
using System;
using System.IO;
using System.IO.Compression;
using System.Xml;
using System.Reflection;
using System.Globalization;
using System.ComponentModel;

using System.Windows;
using System.Windows.Media;
using System.Windows.Markup;
using System.Windows.Controls;
using System.Windows.Resources;

using SharpVectors.Runtime;
using SharpVectors.Renderers.Wpf;

namespace SharpVectors.Converters
{
    /// <summary>
    /// This is a <see cref="Viewbox"/> control for viewing SVG file in WPF
    /// applications.
    /// </summary>
    /// <remarks>
    /// It wraps the drawing canvas, <see cref="SvgDrawingCanvas"/>, instead of
    /// image control, therefore any interactivity support implemented in the
    /// drawing canvas will be available in the <see cref="Viewbox"/>.
    /// </remarks>
    public class SvgViewbox : Viewbox, IUriContext
    {
        #region Private Fields

        private bool _isAutoSized;
        private bool _autoSize;
        private bool _textAsGeometry;
        private bool _includeRuntime;
        private bool _optimizePath;

        private DrawingGroup _svgDrawing;

        private CultureInfo _culture;

        private Uri _baseUri;
        private Uri _sourceUri;

        private SvgDrawingCanvas _drawingCanvas;

        #endregion

        #region Constructors and Destructor

        /// <summary>
        /// Initializes a new instance of the <see cref="SvgViewbox"/> class.
        /// </summary>
        public SvgViewbox()
        {
            _textAsGeometry = false;
            _includeRuntime = true;
            _optimizePath   = true;
            _drawingCanvas  = new SvgDrawingCanvas();

            this.Child      = _drawingCanvas;
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the path to the SVG file to load into this 
        /// <see cref="Viewbox"/>.
        /// </summary>
        /// <value>
        /// A <see cref="System.Uri"/> specifying the path to the SVG source file.
        /// The file can be located on a computer, network or assembly resources.
        /// Settings this to <see langword="null"/> will close any opened diagram.
        /// </value>
        public Uri Source
        {
            get
            {
                return _sourceUri;
            }
            set
            {
                _sourceUri = value;

                if (_sourceUri == null)
                {
                    this.OnUnloadDiagram();
                }
                else
                {
                    this.OnSettingsChanged();
                }
            }
        }

        /// <summary>
        /// Gets the drawing canvas, which is the child of this <see cref="Viewbox"/>.
        /// </summary>
        /// <value>
        /// An instance of the <see cref="SvgDrawingCanvas"/> specifying the child
        /// of this <see cref="Viewbox"/>, which handles the rendering.
        /// </value>
        public Canvas DrawingCanvas
        {
            get
            {
                return _drawingCanvas;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether to automatically resize this
        /// <see cref="Viewbox"/> based on the size of the loaded drawing.
        /// </summary>
        /// <value>
        /// This is <see langword="true"/> if this <see cref="Viewbox"/> is
        /// automatically resized based on the size of the loaded drawing;
        /// otherwise, it is <see langword="false"/>. The default is 
        /// <see langword="false"/>, and the user-defined size or the parent assigned
        /// layout size is used.
        /// </value>
        public bool AutoSize
        {
            get
            {
                return _autoSize;
            }
            set
            {
                _autoSize = value;

                this.OnAutoSizeChanged();
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the path geometry is 
        /// optimized using the <see cref="StreamGeometry"/>.
        /// </summary>
        /// <value>
        /// This is <see langword="true"/> if the path geometry is optimized
        /// using the <see cref="StreamGeometry"/>; otherwise, it is 
        /// <see langword="false"/>. The default is <see langword="true"/>.
        /// </value>
        public bool OptimizePath
        {
            get
            {
                return _optimizePath;
            }
            set
            {
                _optimizePath = value;

                this.OnSettingsChanged();
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the texts are rendered as
        /// path geometry.
        /// </summary>
        /// <value>
        /// This is <see langword="true"/> if texts are rendered as path 
        /// geometries; otherwise, this is <see langword="false"/>. The default
        /// is <see langword="false"/>.
        /// </value>
        public bool TextAsGeometry
        {
            get
            {
                return _textAsGeometry;
            }
            set
            {
                _textAsGeometry = value;

                this.OnSettingsChanged();
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the <c>SharpVectors.Runtime.dll</c>
        /// classes are used in the generated output.
        /// </summary>
        /// <value>
        /// This is <see langword="true"/> if the <c>SharpVectors.Runtime.dll</c>
        /// classes and types are used in the generated output; otherwise, it is 
        /// <see langword="false"/>. The default is <see langword="true"/>.
        /// </value>
        /// <remarks>
        /// The use of the <c>SharpVectors.Runtime.dll</c> prevents the hard-coded
        /// font path generated by the <see cref="FormattedText"/> class, support
        /// for embedded images etc.
        /// </remarks>
        public bool IncludeRuntime
        {
            get
            {
                return _includeRuntime;
            }
            set
            {
                _includeRuntime = value;

                this.OnSettingsChanged();
            }
        }

        /// <summary>
        /// Gets or sets the main culture information used for rendering texts.
        /// </summary>
        /// <value>
        /// An instance of the <see cref="CultureInfo"/> specifying the main
        /// culture information for texts. The default is the English culture.
        /// </value>
        /// <remarks>
        /// <para>
        /// This is the culture information passed to the <see cref="FormattedText"/>
        /// class instance for the text rendering.
        /// </para>
        /// <para>
        /// The library does not currently provide any means of splitting texts
        /// into its multi-language parts.
        /// </para>
        /// </remarks>
        public CultureInfo CultureInfo
        {
            get
            {
                return _culture;
            }
            set
            {
                if (value != null)
                {
                    _culture = value;

                    this.OnSettingsChanged();
                }
            }
        }

        #endregion

        #region Public Methods

        #endregion

        #region Protected Methods

        /// <summary>
        /// Raises the Initialized event. This method is invoked whenever IsInitialized is set to true.
        /// </summary>
        /// <param name="e">Event data for the event.</param>
        protected override void OnInitialized(EventArgs e)
        {
            base.OnInitialized(e);

            if (_sourceUri != null)
            {
                if (_svgDrawing == null)
                {
                    DrawingGroup drawing = this.CreateDrawing();
                    if (drawing != null)
                    {
                        this.OnLoadDrawing(drawing);
                    }
                }
            }
        }

        /// <summary>
        /// This handles changes in the rendering settings of this control.
        /// </summary>
        protected virtual void OnSettingsChanged()
        {   
            if (!this.IsInitialized || _sourceUri == null)
            {
                return;
            }

            DrawingGroup drawing = this.CreateDrawing();
            if (drawing != null)
            {
                this.OnLoadDrawing(drawing);
            }
        }

        /// <summary>
        /// This handles changes in the automatic resizing property of this control.
        /// </summary>
        protected virtual void OnAutoSizeChanged()
        {   
            if (_autoSize)
            {
                if (this.IsInitialized && _svgDrawing != null)
                {
                    Rect rectDrawing = _svgDrawing.Bounds;
                    if (!rectDrawing.IsEmpty)
                    {
                        this.Width   = rectDrawing.Width;
                        this.Height  = rectDrawing.Height;

                        _isAutoSized = true;
                    }
                }
            }
            else
            {
                if (_isAutoSized)
                {
                    this.Width  = Double.NaN;
                    this.Height = Double.NaN;
                }
            }
        }

        /// <summary>
        /// Performs the conversion of a valid SVG source file to the 
        /// <see cref="DrawingGroup"/>.
        /// </summary>
        /// <returns>
        /// This returns <see cref="DrawingGroup"/> if successful; otherwise, it
        /// returns <see langword="null"/>.
        /// </returns>
        protected virtual DrawingGroup CreateDrawing()
        {
            Uri svgSource = this.GetAbsoluteUri();

            DrawingGroup drawing = null;

            if (svgSource == null)
            {
                return drawing;
            }

            try
            {
                string scheme = svgSource.Scheme;
                if (String.IsNullOrEmpty(scheme))
                {
                    return null;
                }

                WpfDrawingSettings settings = new WpfDrawingSettings();
                settings.IncludeRuntime = _includeRuntime;
                settings.TextAsGeometry = _textAsGeometry;
                settings.OptimizePath   = _optimizePath;
                if (_culture != null)
                {
                    settings.CultureInfo = _culture;
                }

                switch (scheme)
                {
                    case "file":
                    //case "ftp":
                    //case "https":
                    case "http":
                        using (FileSvgReader reader =
                            new FileSvgReader(settings))
                        {
                            drawing = reader.Read(svgSource);
                        }
                        break;
                    case "pack":
                        StreamResourceInfo svgStreamInfo = null;
                        if (svgSource.ToString().IndexOf("siteoforigin",
                            StringComparison.OrdinalIgnoreCase) >= 0)
                        {
                            svgStreamInfo = Application.GetRemoteStream(svgSource);
                        }
                        else
                        {
                            svgStreamInfo = Application.GetResourceStream(svgSource);
                        }

                        Stream svgStream = (svgStreamInfo != null) ?
                            svgStreamInfo.Stream : null;

                        if (svgStream != null)
                        {
                            string fileExt = Path.GetExtension(svgSource.ToString());
                            bool isCompressed = !String.IsNullOrEmpty(fileExt) &&
                                String.Equals(fileExt, ".svgz",
                                StringComparison.OrdinalIgnoreCase);

                            if (isCompressed)
                            {
                                using (svgStream)
                                {
                                    using (GZipStream zipStream =
                                        new GZipStream(svgStream, CompressionMode.Decompress))
                                    {
                                        using (FileSvgReader reader =
                                            new FileSvgReader(settings))
                                        {
                                            drawing = reader.Read(zipStream);
                                        }
                                    }
                                }
                            }
                            else
                            {
                                using (svgStream)
                                {
                                    using (FileSvgReader reader =
                                        new FileSvgReader(settings))
                                    {
                                        drawing = reader.Read(svgStream);
                                    }
                                }
                            }
                        }
                        break;
                }

            }
            catch
            {
                if (DesignerProperties.GetIsInDesignMode(new DependencyObject()) ||
                    LicenseManager.UsageMode == LicenseUsageMode.Designtime)
                {
                    return drawing;
                }

                throw;
            }

            return drawing;
        }

        #endregion

        #region Private Methods

        private void OnLoadDrawing(DrawingGroup drawing)
        {   
            if (drawing == null || _drawingCanvas == null)
            {
                return;
            }

            this.OnUnloadDiagram();

            _drawingCanvas.RenderDiagrams(drawing);

            _svgDrawing = drawing;

            this.OnAutoSizeChanged();
        }

        private void OnUnloadDiagram()
        {
            if (_drawingCanvas != null)
            {
                _drawingCanvas.UnloadDiagrams();

                if (_isAutoSized)
                {
                    this.Width  = Double.NaN;
                    this.Height = Double.NaN;
                }
            }
        }

        private Uri GetAbsoluteUri()
        {
            if (_sourceUri == null)
            {
                return null;
            }
            Uri svgSource = _sourceUri;

            if (svgSource.IsAbsoluteUri)
            {
                return svgSource;
            }
            else
            {
                // Try getting a local file in the same directory....
                string svgPath = svgSource.ToString();
                if (svgPath[0] == '\\' || svgPath[0] == '/')
                {
                    svgPath = svgPath.Substring(1);
                }
                svgPath = svgPath.Replace('/', '\\');

                Assembly assembly = Assembly.GetExecutingAssembly();
                string localFile = Path.Combine(Path.GetDirectoryName(
                    assembly.Location), svgPath);

                if (File.Exists(localFile))
                {
                    return new Uri(localFile);
                }

                // Try getting it as resource file...
                if (_baseUri != null)
                {
                    return new Uri(_baseUri, svgSource);
                }
                else
                {
                    string asmName = assembly.GetName().Name;
                    string uriString = String.Format(
                        "pack://application:,,,/{0};component/{1}",
                        asmName, svgPath);

                    return new Uri(uriString);
                }
            }
        }

        #endregion

        #region IUriContext Members

        /// <summary>
        /// Gets or sets the base URI of the current application context.
        /// </summary>
        /// <value>
        /// The base URI of the application context.
        /// </value>
        public Uri BaseUri
        {
            get
            {
                return _baseUri;
            }
            set
            {
                _baseUri = value;
            }
        }

        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The BSD License


Written By
Engineer
Japan Japan
Systems Engineer

Comments and Discussions