Commit 33ccfa70 authored by davescriven's avatar davescriven
Browse files

- Bug fix to SvgHandler that caused exceptions to be thrown when ?raw=true was in the querystring

- Fixed support for text-anchor="middle" and text-anchor="end" for <text/> elements.
- Added more XML API documentation
- Added x attribute and y attribute support to <use/> elements.
parent a37ad025
......@@ -15,9 +15,6 @@ namespace Svg
public class SvgCircle : SvgGraphicsElement
{
private GraphicsPath _path;
private SvgUnit _cx;
private SvgUnit _cy;
private SvgUnit _radius;
/// <summary>
/// Gets the center point of the circle.
......@@ -35,13 +32,16 @@ namespace Svg
[SvgAttribute("cx")]
public SvgUnit CenterX
{
get { return this._cx; }
get { return this.Attributes.GetAttribute<SvgUnit>("cx"); }
set
{
this._cx = value;
if (this.Attributes.GetAttribute<SvgUnit>("cx") != value)
{
this.Attributes["cx"] = value;
this.IsPathDirty = true;
}
}
}
/// <summary>
/// Gets or sets the center Y co-ordinate.
......@@ -50,13 +50,16 @@ namespace Svg
[SvgAttribute("cy")]
public SvgUnit CenterY
{
get { return this._cy; }
get { return this.Attributes.GetAttribute<SvgUnit>("cy"); }
set
{
this._cy = value;
if (this.Attributes.GetAttribute<SvgUnit>("cy") != value)
{
this.Attributes["cy"] = value;
this.IsPathDirty = true;
}
}
}
/// <summary>
/// Gets or sets the radius of the circle.
......@@ -65,18 +68,20 @@ namespace Svg
[SvgAttribute("r")]
public SvgUnit Radius
{
get { return this._radius; }
get { return this.Attributes.GetAttribute<SvgUnit>("r"); }
set
{
this._radius = value;
if (this.Attributes.GetAttribute<SvgUnit>("r") != value)
{
this.Attributes["r"] = value;
this.IsPathDirty = true;
}
}
}
/// <summary>
/// Gets the name of the element.
/// </summary>
/// <value></value>
protected override string ElementName
{
get { return "circle"; }
......@@ -85,7 +90,7 @@ namespace Svg
/// <summary>
/// Gets the bounds of the circle.
/// </summary>
/// <value>The bounds.</value>
/// <value>The rectangular bounds of the circle.</value>
public override RectangleF Bounds
{
get { return this.Path.GetBounds(); }
......@@ -102,6 +107,9 @@ namespace Svg
get { return true; }
}
/// <summary>
/// Gets the <see cref="GraphicsPath"/> representing this element.
/// </summary>
public override GraphicsPath Path
{
get
......
......@@ -8,42 +8,43 @@ namespace Svg
{
public sealed class SvgClipPath : SvgElement
{
private SvgCoordinateSystem _clipPathUnits;
private SvgCoordinateUnits _clipPathUnits;
private bool _pathDirty;
private Region _region;
[SvgAttribute("clipPathUnits")]
public SvgCoordinateSystem ClipPathUnits
public SvgCoordinateUnits ClipPathUnits
{
get { return this._clipPathUnits; }
set { this._clipPathUnits = value; }
}
/// <summary>
/// Initializes a new instance of the <see cref="SvgClipPath"/> class.
/// </summary>
public SvgClipPath()
{
this._clipPathUnits = SvgCoordinateSystem.UserSpaceOnUse;
this._clipPathUnits = SvgCoordinateUnits.ObjectBoundingBox;
}
public override string ElementName
/// <summary>
/// Gets the name of the element.
/// </summary>
protected override string ElementName
{
get { return "clipPath"; }
}
public override object Clone()
{
SvgClipPath path = new SvgClipPath();
path._clipPathUnits = this._clipPathUnits;
return path;
}
public Region GetClipRegion()
private Region GetClipRegion()
{
if (_region == null || _pathDirty)
{
_region = new Region();
foreach (SvgElement element in this.Children)
{
ComplementRegion(_region, element);
}
_pathDirty = false;
}
......@@ -56,21 +57,25 @@ namespace Svg
SvgGraphicsElement graphicsElement = element as SvgGraphicsElement;
if (graphicsElement != null)
{
region.Complement(graphicsElement.Path);
}
foreach (SvgElement child in element.Children)
{
ComplementRegion(region, element);
}
}
protected override void AddedElement(SvgElement child, int index)
protected override void ElementAdded(SvgElement child, int index)
{
base.AddedElement(child, index);
base.ElementAdded(child, index);
this._pathDirty = true;
}
protected override void RemovedElement(SvgElement child)
protected override void ElementRemoved(SvgElement child)
{
base.RemovedElement(child);
base.ElementRemoved(child);
this._pathDirty = true;
}
......
......@@ -6,9 +6,5 @@ namespace Svg
{
public class SvgMask : SvgElement
{
public override object Clone()
{
throw new Exception("The method or operation is not implemented.");
}
}
}
\ No newline at end of file
......@@ -17,7 +17,7 @@ namespace Svg
private bool _isEmpty;
private float? _deviceValue;
public static readonly SvgUnit Empty = new SvgUnit(0.0f);
public static readonly SvgUnit Empty = new SvgUnit();
/// <summary>
/// Gets a value to determine whether the unit is empty.
......@@ -114,10 +114,14 @@ namespace Svg
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (!(obj.GetType() == typeof(SvgUnit)))
{
return false;
}
SvgUnit unit = (SvgUnit)obj;
return (unit.Value == this.Value && unit.Type == this.Type);
......
......@@ -211,7 +211,8 @@ namespace Svg
using (Graphics g = Graphics.FromImage(bitmap))
{
g.TextContrast = 0;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
g.TextContrast = 1;
g.PixelOffsetMode = PixelOffsetMode.Half;
this.Render(g);
g.Save();
......
......@@ -4,6 +4,7 @@ using System.Text;
using System.Web;
using System.Xml;
using System.Xml.Serialization;
using System.Drawing.Drawing2D;
namespace Svg
{
......@@ -18,6 +19,26 @@ namespace Svg
set { this._referencedElement = value; }
}
[SvgAttribute("x")]
public virtual SvgUnit X
{
get { return this.Attributes.GetAttribute<SvgUnit>("x"); }
set { this.Attributes["x"] = value; }
}
[SvgAttribute("y")]
public virtual SvgUnit Y
{
get { return this.Attributes.GetAttribute<SvgUnit>("y"); }
set { this.Attributes["y"] = value; }
}
protected internal override void PushTransforms(System.Drawing.Graphics graphics)
{
base.PushTransforms(graphics);
graphics.TranslateTransform(this.X.ToDeviceValue(this), this.Y.ToDeviceValue(this, true));
}
public SvgUse()
{
......
......@@ -28,9 +28,8 @@ namespace Svg
{
int alpha = (int)((opacity * (this.Colour.A/255) ) * 255);
Color colour = Color.FromArgb(alpha, this.Colour);
SolidBrush brush = new SolidBrush(colour);
return brush;
return new SolidBrush(colour);
}
public override string ToString()
......@@ -39,7 +38,9 @@ namespace Svg
// Return the name if it exists
if (c.IsKnownColor)
{
return c.Name;
}
// Return the hex value
return String.Format("#{0}", c.ToArgb().ToString("x").Substring(2));
......
......@@ -9,14 +9,14 @@ namespace Svg
{
public abstract class SvgGradientServer : SvgPaintServer
{
private SvgGradientUnit _gradientUnits;
private SvgCoordinateUnits _gradientUnits;
private SvgGradientSpreadMethod _spreadMethod = SvgGradientSpreadMethod.Pad;
private SvgGradientServer _inheritGradient;
private List<SvgGradientStop> _stops;
internal SvgGradientServer()
{
this.GradientUnits = SvgGradientUnit.ObjectBoundingBox;
this.GradientUnits = SvgCoordinateUnits.ObjectBoundingBox;
this._stops = new List<SvgGradientStop>();
}
......@@ -47,7 +47,7 @@ namespace Svg
}
[SvgAttribute("gradientUnits")]
public SvgGradientUnit GradientUnits
public SvgCoordinateUnits GradientUnits
{
get { return this._gradientUnits; }
set { this._gradientUnits = value; }
......
......@@ -84,11 +84,13 @@ namespace Svg
{
// Need at least 2 colours to do the gradient fill
if (this.Stops.Count < 2)
{
return null;
}
PointF start;
PointF end;
RectangleF bounds = (this.GradientUnits == SvgGradientUnit.ObjectBoundingBox) ? owner.Bounds : owner.OwnerDocument.GetDimensions();
RectangleF bounds = (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) ? owner.Bounds : owner.OwnerDocument.GetDimensions();
// Have start/end points been set? If not the gradient is horizontal
if (!this.End.IsEmpty())
......
......@@ -64,7 +64,7 @@ namespace Svg
float left = this.CenterX.ToDeviceValue(renderingElement);
float top = this.CenterY.ToDeviceValue(renderingElement, true);
float radius = this.Radius.ToDeviceValue(renderingElement);
RectangleF boundingBox = (this.GradientUnits == SvgGradientUnit.ObjectBoundingBox) ? renderingElement.Bounds : renderingElement.OwnerDocument.GetDimensions();
RectangleF boundingBox = (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) ? renderingElement.Bounds : renderingElement.OwnerDocument.GetDimensions();
path.AddEllipse(left-radius, top-radius, radius*2, radius*2);
......
......@@ -71,8 +71,12 @@
<Compile Include="Basic Shapes\SvgLine.cs" />
<Compile Include="Basic Shapes\SvgPolygon.cs" />
<Compile Include="Basic Shapes\SvgPolyline.cs" />
<Compile Include="Clipping and Masking\ISvgClipable.cs" />
<Compile Include="Clipping and Masking\SvgClipPath.cs" />
<Compile Include="Clipping and Masking\SvgMask.cs" />
<Compile Include="DataTypes\ISvgViewPort.cs" />
<Compile Include="DataTypes\SvgElementStyle.cs" />
<Compile Include="DataTypes\SvgCoordinateUnits.cs" />
<Compile Include="DataTypes\SvgUnitCollection.cs" />
<Compile Include="DataTypes\SvgViewBox.cs" />
<Compile Include="Document Structure\SvgDefinitionList.cs" />
......@@ -89,7 +93,6 @@
<Compile Include="ISvgRenderer.cs" />
<Compile Include="Painting\SvgColourConverter.cs" />
<Compile Include="Painting\SvgGradientSpreadMethod.cs" />
<Compile Include="Painting\SvgGradientUnit.cs" />
<Compile Include="SvgDtdResolver.cs" />
<Compile Include="Exceptions\SvgException.cs" />
<Compile Include="Painting\SvgFillRule.cs" />
......@@ -160,7 +163,6 @@
<EmbeddedResource Include="Resources\svg11.dtd" />
</ItemGroup>
<ItemGroup>
<Folder Include="Clipping and Masking\" />
<Folder Include="Web\Resources\" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
......
......@@ -15,7 +15,6 @@ namespace Svg
public abstract class SvgElement : ISvgElement, ISvgTransformable, ICloneable
{
internal SvgElement _parent;
private string _content;
private string _elementName;
private SvgAttributeCollection _attributes;
private EventHandlerList _eventHandlers;
......@@ -36,8 +35,8 @@ namespace Svg
/// </summary>
public virtual string Content
{
get { return this._content; }
set { this._content = value; }
get;
set;
}
/// <summary>
......@@ -93,14 +92,20 @@ namespace Svg
if (Parent == null)
{
if (this is SvgDocument)
{
return (SvgDocument)this;
}
else
{
return null;
}
}
else
{
return Parent.OwnerDocument;
}
}
}
/// <summary>
/// Gets a collection of element attributes.
......@@ -120,14 +125,14 @@ namespace Svg
protected internal virtual void PushTransforms(Graphics graphics)
{
_graphicsMatrix = graphics.Transform;
// Return if there are no transforms
if (this.Transforms == null || this.Transforms.Count == 0)
{
return;
}
_graphicsMatrix = graphics.Transform;
Matrix transformMatrix = new Matrix();
foreach (SvgTransform transformation in this.Transforms)
......@@ -140,11 +145,6 @@ namespace Svg
protected internal virtual void PopTransforms(Graphics graphics)
{
if (this.Transforms == null || this.Transforms.Count == 0 || _graphicsMatrix == null)
{
return;
}
graphics.Transform = _graphicsMatrix;
_graphicsMatrix = null;
}
......@@ -182,7 +182,9 @@ namespace Svg
{
// Don't do anything if it hasn't changed
if (string.Compare(this.ID, value) == 0)
{
return;
}
if (this.OwnerDocument != null)
{
......
......@@ -52,8 +52,10 @@ namespace Svg
SvgElement element = this[index];
if (element != null)
{
this.Remove(element);
}
}
public SvgElement this[int index]
{
......
......@@ -59,6 +59,9 @@ namespace Svg
case "desc":
createdElement = new SvgDescription();
break;
case "clipPath":
createdElement = new SvgClipPath();
break;
case "svg":
if (!fragmentIsDocument)
fragment = new SvgFragment();
......@@ -156,132 +159,5 @@ namespace Svg
}
}
}
//private static void SetAttributes(SvgElement element, Dictionary<string, string> attributes, SvgDocument document)
//{
// // Parse attributes
// foreach(KeyValuePair<string, string> keyValuePair in attributes)
// {
// string name = keyValuePair.Key;
// string value = keyValuePair.Value;
// switch (name)
// {
// case "id":
// if (!String.IsNullOrEmpty(value))
// SetProperty(element, name, value);
// break;
// case "style":
// string[] styles = value.Split(';');
// Dictionary<string, string> styleAttributes = new Dictionary<string, string>();
// foreach (string style in styles)
// {
// if (String.IsNullOrEmpty(style) || style.IndexOf(":") == -1)
// continue;
// string[] pair = style.Split(':');
// styleAttributes.Add(pair[0].Trim(), pair[1].Trim());
// }
// SetAttributes(element, styleAttributes, document);
// break;
// case "href":
// if (element is SvgUse)
// SetProperty(element, name, document.GetElementById(value));
// break;
// case "transform":
// SetProperty(element, name, _transformConverter.ConvertFrom(value));
// break;
// case "stroke":
// case "fill":
// SetProperty(element, name, SvgPaintServerFactory.Create(value, document));
// break;
// case "font":
// break;
// case "font-family":
// // TODO: create font family converter, loop through families list. return generic if it's not in the list
// try
// {
// SetProperty(element, name, new FontFamily(value));
// }
// catch
// {
// Trace.TraceWarning("\"{0}\" is not a recognised font.", value);
// SetProperty(element, name, FontFamily.GenericSansSerif);
// }
// break;
// case "font-weight":
// //SetProperty(createdElement, reader.LocalName, reader.Value);
// break;
// case "fill-opacity":
// case "stroke-opacity":
// case "stop-opacity":
// case "opacity":
// SetProperty(element, name, float.Parse(value));
// break;
// case "points":
// // TODO: TypeConverter for this?
// string points = value.Replace(",", " ").Trim();
// Regex spaceReplace = new Regex(@"\s+");
// points = spaceReplace.Replace(points, " ");
// string[] pts = points.Split(' ');
// List<SvgUnit> units = new List<SvgUnit>();
// foreach (string point in pts)
// units.Add((SvgUnit)_unitConverter.ConvertFrom(point));
// SetProperty(element, name, units);
// break;
// case "font-size":
// case "letter-spacing":
// case "word-spacing":
// case "r":
// case "width":
// case "height":
// case "ry":
// case "rx":
// case "x":
// case "y":
// case "x1":
// case "y1":
// case "x2":
// case "y2":
// case "cy":
// case "cx":
// case "offset":
// case "stroke-width":
// SetProperty(element, name, (SvgUnit)_unitConverter.ConvertFrom(value));
// break;
// case "stop-color":
// SetProperty(element, name, (Color)_colourConverter.ConvertFrom(value));
// break;
// case "d":
// SvgPathBuilder.Parse(value, ((SvgPath)element).PathData);
// break;
// case "pathLength":
// SetProperty(element, name, int.Parse(value));
// break;
// default:
// break;
// }
// }
//}
//private static void SetProperty(object element, string attributeName, object attributeValue)
//{
// string key = String.Format("{0}{1}", element.GetType().Name, attributeName);
// if (!_propertyDescriptorLookup.ContainsKey(key))
// {
// PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(element.GetType(), new Attribute[] { new SvgAttributeAttribute(attributeName) });
// if (properties.Count == 0)
// return;
// _propertyDescriptorLookup.Add(key, properties[0]);
// }
// PropertyDescriptor property = _propertyDescriptorLookup[key];
// property.SetValue(element, attributeValue);
//}
}
}
\ No newline at end of file
......@@ -21,7 +21,9 @@ namespace Svg
public virtual SvgElement GetElementById(string id)
{
if (id.StartsWith("#"))
{
id = id.Substring(1);
}
return this._idValueMap[id];
}
......
......@@ -30,6 +30,7 @@ namespace Svg
{
Bitmap bitmap = new Bitmap(1, 1);
_stringMeasure = Graphics.FromImage(bitmap);
_stringMeasure.TextRenderingHint = TextRenderingHint.AntiAlias;
}
/// <summary>
......@@ -193,6 +194,14 @@ namespace Svg
base.Render(graphics);
}
static private int MeasureString(Graphics graphics, string text, Font font)
{
GraphicsPath p = new GraphicsPath();
p.AddString(text, font.FontFamily, 0, font.Size, new PointF(0.0f, 0.0f), StringFormat.GenericTypographic);
p.Transform(graphics.Transform);
return (int)(p.GetBounds().Width + 1.0f);
}
/// <summary>
/// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary>
......@@ -205,7 +214,7 @@ namespace Svg
if (_path == null || this.IsPathDirty && !string.IsNullOrEmpty(this.Text))
{
float fontSize = this.FontSize.ToDeviceValue(this);
SizeF stringSize = SizeF.Empty;
int stringWidth;
PointF location = PointF.Empty;
// Minus FontSize because the x/y coords mark the bottom left, not bottom top.
......@@ -215,12 +224,12 @@ namespace Svg
location = new PointF(this.X.ToDeviceValue(this), this.Y.ToDeviceValue(this, true) - fontSize);
break;
case SvgTextAnchor.Middle:
stringSize = _stringMeasure.MeasureString(this.Text, new Font(this._font.FontFamily, fontSize));
location = new PointF(this.X.ToDeviceValue(this) - (stringSize.Width / 2), this.Y.ToDeviceValue(this, true) - fontSize);
stringWidth = SvgText.MeasureString(_stringMeasure, this.Text, new Font(this._font.FontFamily, fontSize));
location = new PointF(this.X.ToDeviceValue(this) - (stringWidth / 2), this.Y.ToDeviceValue(this, true) - fontSize);
break;
case SvgTextAnchor.End:
stringSize = _stringMeasure.MeasureString(this.Text, new Font(this._font.FontFamily, fontSize));
location = new PointF(this.X.ToDeviceValue(this) - stringSize.Width, this.Y.ToDeviceValue(this, true) - fontSize);
stringWidth = SvgText.MeasureString(_stringMeasure, this.Text, new Font(this._font.FontFamily, fontSize));
location = new PointF(this.X.ToDeviceValue(this) - stringWidth, this.Y.ToDeviceValue(this, true) - fontSize);
break;
}
......@@ -244,12 +253,14 @@ namespace Svg
char[] characters = word.ToCharArray();
foreach (char currentCharacter in characters)
{
_path.AddString(currentCharacter.ToString(), this._font.FontFamily, 0, fontSize, location, StringFormat.GenericDefault);
_path.AddString(currentCharacter.ToString(), this._font.FontFamily, 0, fontSize, location, StringFormat.GenericTypographic);
location = new PointF(_path.GetBounds().Width + start + letterSpacing, location.Y);
}
}
else
_path.AddString(word, this._font.FontFamily, 0, fontSize, location, StringFormat.GenericDefault);
{
_path.AddString(word, this._font.FontFamily, 0, fontSize, location, StringFormat.GenericTypographic);
}
// Move the location of the word to be written along
location = new PointF(_path.GetBounds().Width + start + wordSpacing, location.Y);
......@@ -257,7 +268,7 @@ namespace Svg
}
else
{
_path.AddString(this.Text, this._font.FontFamily, 0, fontSize, location, StringFormat.GenericDefault);
_path.AddString(this.Text, this._font.FontFamily, 0, fontSize, location, StringFormat.GenericTypographic);
}
_path.CloseFigure();
......
......@@ -93,7 +93,6 @@ namespace Svg.Web
this._state._context.Response.WriteFile(this._state._context.Request.PhysicalPath);
this._state._context.Response.End();
this._state.CompleteRequest();
return;
}
public void RenderSvg()
......@@ -101,13 +100,15 @@ namespace Svg.Web
this._state._context.Response.AddFileDependency(this._state._context.Request.PhysicalPath);
this._state._context.Response.Cache.SetLastModifiedFromFileDependencies();
this._state._context.Response.Cache.SetETagFromFileDependencies();
this._state._context.Response.Buffer = false;
// Allow crawlers to see the raw XML - they can get more information from it that way
if (this._state._context.Request.Browser.Crawler || !string.IsNullOrEmpty(this._state._context.Request.QueryString["raw"]))
{
this.RenderRawSvg();
}
else
{
try
{
SvgDocument document = SvgDocument.Open(this._state._context.Request.PhysicalPath);
......@@ -133,6 +134,7 @@ namespace Svg.Web
}
}
}
}
/// <summary>
/// Represents the state of a request for SVG rendering.
......@@ -151,8 +153,7 @@ namespace Svg.Web
/// <param name="context">The <see cref="HttpContext"/> of the request.</param>
/// <param name="callback">The delegate to be called when the rendering is complete.</param>
/// <param name="extraData">The extra data.</param>
public SvgAsyncRenderState(HttpContext context, AsyncCallback callback,
object extraData)
public SvgAsyncRenderState(HttpContext context, AsyncCallback callback, object extraData)
{
_context = context;
_callback = callback;
......@@ -167,9 +168,9 @@ namespace Svg.Web
_isCompleted = true;
lock (this)
{
if (_callCompleteEvent != null)
if (this.AsyncWaitHandle != null)
{
_callCompleteEvent.Set();
this._callCompleteEvent.Set();
}
}
// if a callback was registered, invoke it now
......@@ -179,8 +180,6 @@ namespace Svg.Web
}
}
// IAsyncResult
//
/// <summary>
/// Gets a user-defined object that qualifies or contains information about an asynchronous operation.
/// </summary>
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment