using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.IO;
using System.Text;
using System.Xml;
using System.Linq;
using ExCSS;
using Svg.Css;
namespace Svg
{
///
/// The class used to create and load SVG documents.
///
public class SvgDocument : SvgFragment, ITypeDescriptorContext
{
public static readonly int PointsPerInch = 96;
private SvgElementIdManager _idManager;
///
/// Initializes a new instance of the class.
///
public SvgDocument()
{
Ppi = PointsPerInch;
}
///
/// Gets an for this document.
///
protected internal virtual SvgElementIdManager IdManager
{
get
{
if (_idManager == null)
{
_idManager = new SvgElementIdManager(this);
}
return _idManager;
}
}
///
/// Overwrites the current IdManager with a custom implementation.
/// Be careful with this: If elements have been inserted into the document before,
/// you have to take care that the new IdManager also knows of them.
///
///
public void OverwriteIdManager(SvgElementIdManager manager)
{
_idManager = manager;
}
///
/// Gets or sets the Pixels Per Inch of the rendered image.
///
public int Ppi { get; set; }
#region ITypeDescriptorContext Members
IContainer ITypeDescriptorContext.Container
{
get { throw new NotImplementedException(); }
}
object ITypeDescriptorContext.Instance
{
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
///
/// Retrieves the with the specified ID.
///
/// A containing the ID of the element to find.
/// An of one exists with the specified ID; otherwise false.
public virtual SvgElement GetElementById(string id)
{
return IdManager.GetElementById(id);
}
///
/// Retrieves the with the specified ID.
///
/// A containing the ID of the element to find.
/// An of one exists with the specified ID; otherwise false.
public virtual TSvgElement GetElementById(string id) where TSvgElement : SvgElement
{
return (this.GetElementById(id) as TSvgElement);
}
///
/// Opens the document at the specified path and loads the SVG contents.
///
/// A containing the path of the file to open.
/// An with the contents loaded.
/// The document at the specified cannot be found.
public static SvgDocument Open(string path)
{
return Open(path, null);
}
///
/// Opens the document at the specified path and loads the SVG contents.
///
/// A containing the path of the file to open.
/// An with the contents loaded.
/// The document at the specified cannot be found.
public static T Open(string path) where T : SvgDocument, new()
{
return Open(path, null);
}
///
/// Opens the document at the specified path and loads the SVG contents.
///
/// A containing the path of the file to open.
/// A dictionary of custom entity definitions to be used when resolving XML entities within the document.
/// An with the contents loaded.
/// The document at the specified cannot be found.
public static T Open(string path, Dictionary entities) where T : SvgDocument, new()
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException("path");
}
if (!File.Exists(path))
{
throw new FileNotFoundException("The specified document cannot be found.", path);
}
return Open(File.OpenRead(path), entities);
}
///
/// Attempts to open an SVG document from the specified .
///
/// The containing the SVG document to open.
public static T Open(Stream stream) where T : SvgDocument, new()
{
return Open(stream, null);
}
///
/// Attempts to create an SVG document from the specified string data.
///
/// The SVG data.
public static T FromSvg(string svg) where T : SvgDocument, new()
{
if (string.IsNullOrEmpty(svg))
{
throw new ArgumentNullException("svg");
}
using (var strReader = new System.IO.StringReader(svg))
{
var reader = new SvgTextReader(strReader, null);
reader.XmlResolver = new SvgDtdResolver();
reader.WhitespaceHandling = WhitespaceHandling.None;
return Open(reader);
}
}
///
/// Opens an SVG document from the specified and adds the specified entities.
///
/// The containing the SVG document to open.
/// Custom entity definitions.
/// The parameter cannot be null.
public static T Open(Stream stream, Dictionary entities) where T : SvgDocument, new()
{
if (stream == null)
{
throw new ArgumentNullException("stream");
}
// Don't close the stream via a dispose: that is the client's job.
var reader = new SvgTextReader(stream, entities);
reader.XmlResolver = new SvgDtdResolver();
reader.WhitespaceHandling = WhitespaceHandling.None;
return Open(reader);
}
private static T Open(XmlReader reader) where T : SvgDocument, new()
{
var elementStack = new Stack();
bool elementEmpty;
SvgElement element = null;
SvgElement parent;
T svgDocument = null;
var styles = new List();
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)
elementEmpty = reader.IsEmptyElement;
// Create element
if (elementStack.Count > 0)
{
element = SvgElementFactory.CreateElement(reader, svgDocument);
}
else
{
svgDocument = SvgElementFactory.CreateDocument(reader);
element = svgDocument;
}
// Add to the parents children
if (elementStack.Count > 0)
{
parent = elementStack.Peek();
if (parent != null && element != null)
{
parent.Children.Add(element);
parent.Nodes.Add(element);
}
}
// Push element into stack
elementStack.Push(element);
// Need to process if the element is empty
if (elementEmpty)
{
goto case XmlNodeType.EndElement;
}
break;
case XmlNodeType.EndElement:
// Pop the element out of the stack
element = elementStack.Pop();
if (element.Nodes.OfType().Any())
{
element.Content = (from e in element.Nodes select e.Content).Aggregate((p, c) => p + c);
}
else
{
element.Nodes.Clear(); // No sense wasting the space where it isn't needed
}
var unknown = element as SvgUnknownElement;
if (unknown != null && unknown.ElementName == "style")
{
styles.Add(unknown);
}
break;
case XmlNodeType.CDATA:
case XmlNodeType.Text:
element = elementStack.Peek();
element.Nodes.Add(new SvgContentNode() { Content = reader.Value });
break;
case XmlNodeType.EntityReference:
reader.ResolveEntity();
element = elementStack.Peek();
element.Nodes.Add(new SvgContentNode() { Content = reader.Value });
break;
}
}
catch (Exception exc)
{
Trace.TraceError(exc.Message);
}
}
if (styles.Any())
{
var cssTotal = styles.Select((s) => s.Content).Aggregate((p, c) => p + Environment.NewLine + c);
var cssParser = new Parser();
var sheet = cssParser.Parse(cssTotal);
IEnumerable elemsToStyle;
foreach (var rule in sheet.StyleRules)
{
elemsToStyle = svgDocument.QuerySelectorAll(rule.Selector.ToString());
foreach (var elem in elemsToStyle)
{
foreach (var decl in rule.Declarations)
{
elem.AddStyle(decl.Name, decl.Term.ToString(), rule.Selector.GetSpecificity());
}
}
}
}
FlushStyles(svgDocument);
return svgDocument;
}
private static void FlushStyles(SvgElement elem)
{
elem.FlushStyles();
foreach (var child in elem.Children)
{
FlushStyles(child);
}
}
///
/// Opens an SVG document from the specified .
///
/// The containing the SVG document XML.
/// The parameter cannot be null.
public static SvgDocument Open(XmlDocument document)
{
if (document == null)
{
throw new ArgumentNullException("document");
}
using (var stream = new MemoryStream(UTF8Encoding.Default.GetBytes(document.InnerXml)))
{
return Open(stream, null);
}
}
public static Bitmap OpenAsBitmap(string path)
{
return null;
}
public static Bitmap OpenAsBitmap(XmlDocument document)
{
return null;
}
///
/// Renders the to the specified .
///
/// The to render the document with.
/// The parameter cannot be null.
public void Draw(SvgRenderer renderer)
{
if (renderer == null)
{
throw new ArgumentNullException("renderer");
}
this.Render(renderer);
}
///
/// Renders the to the specified .
///
/// The to be rendered to.
/// The parameter cannot be null.
public void Draw(Graphics graphics)
{
if (graphics == null)
{
throw new ArgumentNullException("graphics");
}
this.Render(SvgRenderer.FromGraphics(graphics));
}
///
/// Renders the and returns the image as a .
///
/// A containing the rendered document.
public virtual Bitmap Draw()
{
//Trace.TraceInformation("Begin Render");
var size = GetDimensions();
var bitmap = new Bitmap((int)Math.Ceiling(size.Width), (int)Math.Ceiling(size.Height));
// bitmap.SetResolution(300, 300);
try
{
Draw(bitmap);
}
catch
{
bitmap.Dispose();
throw;
}
//Trace.TraceInformation("End Render");
return bitmap;
}
///
/// Renders the into a given Bitmap .
///
public virtual void Draw(Bitmap bitmap)
{
//Trace.TraceInformation("Begin Render");
try
{
using (var renderer = SvgRenderer.FromImage(bitmap))
{
renderer.TextRenderingHint = TextRenderingHint.AntiAlias;
renderer.TextContrast = 1;
renderer.PixelOffsetMode = PixelOffsetMode.Half;
this.Render(renderer);
renderer.Save();
}
}
catch
{
throw;
}
//Trace.TraceInformation("End Render");
}
public void Write(Stream stream)
{
var xmlWriter = new XmlTextWriter(stream, Encoding.UTF8);
xmlWriter.Formatting = Formatting.Indented;
xmlWriter.WriteDocType("svg", "-//W3C//DTD SVG 1.1//EN", "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd", null);
this.WriteElement(xmlWriter);
xmlWriter.Flush();
}
public void Write(string path)
{
using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write))
{
this.Write(fs);
}
}
}
}